Skip to content

PHPStan ignores

The phpstan-ignores plugin is for a project that also runs PHPStan. It lets a @phpstan-ignore comment drop the Mago issues that report the same finding, so the line doesn't need a @mago-expect on top.

Turning it on

The plugin is off by default. Turn it on in mago.toml:

[analyzer]
plugins = ["phpstan-ignores"]
// @phpstan-ignore return.type (the caller handles NULL)
return NULL;  // no invalid-return-statement

Comment forms

The plugin reads PHPStan's forms:

  • @phpstan-ignore with identifiers separated by commas, and an optional reason in parentheses after any of them. It sits at the end of the line, or above it, where it covers the next line of code;
  • @phpstan-ignore-next-line;
  • @phpstan-ignore-line.

PHPStan wants that code on the very next line, and calls a comment with anything in between unused. The plugin skips blank lines and other comments, so a @mago-expect can sit between the two.

A comment over several lines covers what PHPStan covers. In a /* */ comment, the line forms count from the comment's last line, and when the comment holds both, it only covers the next line. In a docblock, they count from the line that holds the tag, and a @phpstan-ignore-next-line that is the docblock's last tag covers the line after the docblock. A comment that holds a line form doesn't read identifiers from @phpstan-ignore.

return NULL; /* The caller handles NULL.
  @phpstan-ignore-line */            // covers this line, not the `return`

Which issues a comment drops

An identifier drops only the Mago codes listed for it by the plugin, so @phpstan-ignore return.type still lets a missing method on the same line through. The two line forms drop every listed code, and an identifier not listed by the plugin doesn't drop anything.

The list covers PHPStan's identifiers for argument, return and property types, argument counts, undefined symbols, static calls to instance methods, deprecations, always-true and always-false conditions, comparisons, isset(), empty(), ?? and type checks with a known result, operand types, docblock types, property defaults, unreachable code, missing types, offset access, and the values that a foreach, a call, a match, a clone or a destructuring can't take. It also covers the deprecation identifiers of phpstan-deprecation-rules. PHPStan identifiers lists each identifier with its Mago codes.

Not dropped:

  • phpstan-drupal's identifiers, which aren't in the list;
  • the drupal/ and phpunit/ checks. They report after Mago runs the plugin, so a comment never drops one of them;
  • the issues of linter rules. See Linter rules for the ones that port a PHPStan check.

An issue counts as being on the covered line when it starts there. Unreachable code is the one exception. PHPStan reports the first unreachable statement of a block, and Mago reports every one of them, so a comment that ignores deadCode.unreachable also drops Mago's unevaluated-code on the lines after it, up to the end of its block. The block ends at its closing brace or at the endif, endforeach or other end keyword of the alternative syntax. An alternative syntax else: or elseif, and the next case or default label of a switch, end it too. When the covered line holds a return, throw, exit, continue or break statement, the run starts at the statement after it. So on a line such as if ($flag): return; echo 'a';, the run starts at the echo and ends with the if block.

$this->markTestSkipped('Blocked by a core issue.');
// @phpstan-ignore deadCode.unreachable
$result = $this->build();          // no unevaluated-code
$this->assertSame('x', $result);   // none here either

Pragmas

Mago matches @mago-expect pragmas before the plugin runs. So a pragma next to a @phpstan-ignore for the same issue still counts as used: it can be removed, but Mago doesn't point it out.

Linter rules

Some linter rules port a PHPStan check, such as phpstan-drupal's RenderCallbackRule. The plugin runs in the analyzer, so it can't drop their issues. With the --phpstan-ignores worker argument, these rules read @phpstan-ignore comments themselves. It's off by default, as the plugin is, and drupal-core.mago.toml turns it on. See The worker.

The comments cover the same lines as for the plugin. @phpstan-ignore-line and @phpstan-ignore-next-line drop the issues of these rules, and an identifier drops the issues of the rule listed with it:

drupal/weak-hash ports the disallowed calls of core's phpstan.neon.dist, which phpstan-disallowed-calls reports as disallowed.function. The other rules don't read @phpstan-ignore comments. Unlike for the plugin, the issue is dropped before Mago sees it, so a @mago-expect for it is reported as unused.