1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00
Go to file
2016-10-31 16:02:22 -04:00
bin Fix more errors caught by Psalm 2016-10-30 12:46:18 -04:00
examples Add an example template checker 2016-10-29 23:07:13 -04:00
src/Psalm Fix Psalm errors in Psalm 2016-10-31 15:42:20 -04:00
tests Add explicit value tracking 2016-10-30 19:52:35 -04:00
.gitignore Fixing a typo in the .gitignore filename. 2016-10-20 11:52:14 -04:00
composer.json Adding PHPUnit as a dev dependency and a Composer script to run tests. 2016-10-20 11:54:55 -04:00
composer.lock Adding PHPUnit as a dev dependency and a Composer script to run tests. 2016-10-20 11:54:55 -04:00
psalm.xml Removed object-like type descriptor in favour of array{} syntax 2016-10-28 13:24:06 -04:00
PsalmLogo.png Update border 2016-10-29 12:46:34 -04:00
README.md Add contents 2016-10-31 16:02:22 -04:00

logo

Inspects your code and finds errors

...

Dealing with code issues

Code issues in Psalm fall into three categories:

error
this will cause Psalm to print a message, and to ultimately terminate with a non-zero exist status
info
this will cause Psalm to print a message
suppress
this will cause Psalm to ignore the code issue entirely

The third category, suppress, is the one you will probably be most interested in, especially when introducing Psalm to a large codebase.

Suppressing issues

There are two ways to suppress an issue via the Psalm config or via a function docblock.

Config suppression

You can use the <issueHandler> tag in the config file to influence how issues are treated.

<issueHandler>
  <MissingPropertyType errorLevel="suppress" />

  <InvalidReturnType>
    <excludeFiles>
      <directory name="some_bad_directory" /> <!-- all InvalidReturnType issues in this directory are suppressed -->
      <file name="some_bad_file.php" />  <!-- all InvalidReturnType issues in this file are suppressed -->
    </excludeFiles>
  </InvalidReturnType>
</issueHandler>

Docblock suppression

You can also use @psalm-suppress IssueName on a function's docblock to suppress Psalm issues e.g.

/**
 * @psalm-suppress InvalidReturnType
 */
function (int $a) : string {
  return $a;
}

Typing in Psalm

Psalm is able to interpret all PHPDoc type annotations, and use them to further understand the codebase.

Property types vs Assignment typehints

You can use the /** @var Type */ docblock to annotate both property declarations and to help Psalm understand variable assignment.

Property types

You can specify a particular type for an class property in Psalm by using the @var declaration:

/** @var string|null */
public $foo;

When checking $this->foo = $some_variable;, Psalm will check to see whether $some_variable is either string or null and, if neither, emit an issue.

If you leave off the property type docblock, Psalm will emit a MissingPropertyType issue.

Assignment typehints

Consider the following code:

$a = null;

foreach ([1, 2, 3] as $i) {
  if ($a) {
    return $a;
  }
  else {
    $a = $i;
  }
}

Because Psalm scans a file progressively, it cannot tell that return $a produces an integer. Instead it returns knows only that $a is not empty. We can fix this by adding a type hint docblock:

/** @var int|null */
$a = null;

foreach ([1, 2, 3] as $i) {
  if ($a) {
    return $a;
  }
  else {
    $a = $i;
  }
}

This tells Psalm that int is a possible type for $a, and allows it to infer that return $a; produces an integer.

Unlike property types, however, assignment typehints are not binding they can be overridden by a new assignment without Psalm emitting an issue e.g.

/** @var string|null */
$a = foo();
$a = 6; // $a is now typed as an int

You can also use typehints on specific variables e.g.

/** @var string $a */
echo strpos($a, 'hello');

This tells Psalm to assume that $a is a string (though it will still throw an error if $a is undefined).

Typing arrays

In PHP, the array type is commonly used to represent three different data structures:

  • a List

    $a = [1, 2, 3, 4, 5];
    
  • an Associative array

    $a = [0 => 'hello', 5 => 'goodbye'];
    $a = ['a' => 'AA', 'b' => 'BB', 'c' => 'CC']
    
  • makeshift Structs

    $a = ['name' => 'Psalm', 'type' => 'tool'];
    

PHP treats all these arrays the same, essentially (though there are some optimisations under the hood for the first case).

PHPDoc allows you to specify the type of values the array holds with the anootation:

/** @return TValue[] */

where TValue is a union type, but it does not allow you to specify the type of keys.

Psalm uses a syntax borrowed from Java to denote the types of both keys and values:

/** @return array<TKey, TValue> */

Makeshift Structs

Ideally (in the author's opinion), all data would either be encoded as lists, associative arrays, or as well-defined objects. However, PHP arrays are often used as makeshift structs.

Hack (by Facebook) supports this usage by way of the Shape datastructure, but there is no agreed-upon documentation format for such arrays in regular PHP-land.

Psalm solves this by adding another way annotate array types, by using an object-like syntax when describing them.

So, for instance,

$a = ['name' => 'Psalm', 'type' => 'tool'];

is assigned the type array{ name: string, type: string}.

Backwards compatibility

Psalm fully supports PHPDoc's array typing syntax, such that any array typed with TValue[] will be typed in Psalm as array<mixed, TValue>. That also extends to generic type definitions with only one param e.g. array<TValue>, which is equivalent to array<mixed, TValue>.

Checking non-PHP files (e.g. templates)

Psalm supports the ability to check various PHPish files by extending the FileChecker class. For example, if you have a template where the variables are set elsewhere, Psalm can scrape those variables and check the template with those variables pre-populated.

An example TemplateChecker is provided here.

To ensure your custom FileChecker is used, you must update the Psalm fileExtensions config in psalm.xml:

<fileExtensions>
    <extension name=".php" />
    <extension name=".phpt" filetypeHandler="path/to/TemplateChecker.php" />
</fileExtensions>