Docblock Types

Docblock Types That Improve Analysis

Native types stop at array, string and int. Docblocks, read by both analyzers and ignored by the engine, say more: list<T>, array<K, V>, shapes such as array{sku: string}, non-empty-string, positive-int, int<0, 100>, class-string<T>, @template generics (Subsection 4.11.15), @phpstan-type aliases and @phpstan-assert for narrowing functions.

types.php: a CSV line parser with a precise return typePHP
<?php
/** @phpstan-type Line array{sku: non-empty-string, qty: positive-int} */
final class LineParser {
  /**
   * @param list<string> $rows  "sku,qty" pairs from a CSV upload
   * @return list<Line>
   */
  public static function parse(array $rows): array {
    return array_map(fn($r) => ['sku' => strtok($r, ','), 'qty' => (int) strtok(',')], $rows);
  }
}
echo json_encode(LineParser::parse(['BK-SQL-01,2', ',0'])), "\n";

PHP printed [{"sku":"BK-SQL-01","qty":2},{"sku":"0","qty":0}]: SKU "0" with quantity 0, because strtok() skips a leading comma. PHPStan 376,908 at level 10 had already rejected the function:

Output of 233
...
  9      Method LineParser::parse() should return list<array{sku: non-empty-string, qty:   
         int<1, max>}> but returns list<array{sku: non-empty-string|false, qty: int}>.     
...

Validate each row and PHPStan narrows the types itself. Write the type you mean, not the one that silences the error: a lying @var is worse than none.