PHPStan
| Language | PHP |
|---|---|
| Kind | tool |
| Readiness | 🟢 |
| Detector conformance | 🟡 |
| Checked | 2026-09-08, version 2.2.13 |
PHPStan is a static analyser that reads PHP without running it and reports type errors, dead code and any pattern a rule class describes. It is the detector most PHP pipelines already run, and it is the detector both reference toolchains for this method build on, so it is the natural host for a bespoke defence in PHP.
How it is conformant
Clause 4.1 is met in full: a project registers its own rule class under rules: in its configuration, or as a service tagged phpstan.rules.rule where the constructor needs arguments, and the rule can express any pattern the AST exposes (developing rules). Clause 4.2 is met by PHPStan\Testing\RuleTestCase, a PHPUnit base class that runs one rule over one fixture file and asserts the errors it reports, without the project’s own suite (the same page). Clause 4.3 is met by RuleErrorBuilder::identifier(), where the author chooses the string once, and the error identifiers page states that the identifier is printed alongside each error in the default table output and in the machine-readable formats. Clauses 5.1 to 5.4 hold: phpstan analyse accepts one or more file paths, runs locally, and prints its findings to the terminal (command line usage). Clause 6.1 is met for bundled rules, since the catalogue at phpstan.org is keyed on the printed identifier, which is the URL form the clause accepts. Clause 7.1 is met: @phpstan-ignore is a comment on an AST node, so a project rule in PHPStan itself can detect and forbid it, and the baseline is a generated file that is only active whilst the configuration includes it (baseline). Clause 7.2 is met for the inline route by reportIgnoresWithoutComments, which is the option the specification names as the shape of it (ignoring errors).
How it is not conformant
The failing clauses are 6.2 and 6.3. The documentation of PHPStan’s bundled identifiers lives on the website and not in the installed package, so a practitioner offline, behind a proxy or holding an identifier from a log cannot resolve it from disk, and nothing tracks the documentation at the version installed. Clause 7.2 is only partly met, because the baseline page says plainly that a reason is not an option when using the baseline, and an ignoreErrors entry has keys for message, identifier, path and count and none for a justification. Clause 4.4 is not met: nothing fails a rule that reports without an identifier, and the builder accepts a message alone. Clause 6.4 was not verified; no published release gate over the identifier catalogue was found.
Clause by clause
| Document | Clause | Result | Evidence |
|---|---|---|---|
| Detector | 4.1 | Yes | rules: and the phpstan.rules.rule service tag, developing rules |
| Detector | 4.2 | Yes | RuleTestCase runs one rule over one fixture, developing rules |
| Detector | 4.3 | Yes | RuleErrorBuilder::identifier(), printed with each error in every format, error identifiers |
| Detector | 4.4 | No | Nothing fails a rule that reports without an identifier; the builder accepts a message alone |
| Detector | 5.1 | Yes | vendor/bin/phpstan analyse runs locally, command line usage |
| Detector | 5.2 | Yes | Paths may be single files, command line usage |
| Detector | 5.3 | Yes | The table formatter prints findings to the invoking terminal, output format |
| Detector | 5.4 | Yes | No mode is CI only; the same command runs anywhere |
| Detector | 6.1 | Yes | Online catalogue keyed on the printed identifier for bundled rules, error identifiers |
| Detector | 6.2 | No | The catalogue is a website; nothing installed resolves an identifier |
| Detector | 6.3 | No | Bundled rule documentation lives on phpstan.org, not in the package |
| Detector | 6.4 | Not verified | The online catalogue appears complete; no published release gate was found |
| Detector | 7.1 | Yes | @phpstan-ignore is a node comment a project rule can detect; the baseline is active only when included, baseline |
| Detector | 7.2 | Partial | reportIgnoresWithoutComments covers inline only; baseline and ignoreErrors carry no reason, ignoring errors |
Notes for a practitioner
PHPStan alone gives you every mechanism the six clauses of the method need: write the rule class, prove it with RuleTestCase, sweep with phpstan analyse, and give the error an identifier. What it does not give you is on-disk documentation for its own identifiers or any listing of active rules: set reportIgnoresWithoutComments, add a rule of your own that forbids @phpstan-ignore, keep the identifier index in the repository, and expect to build the listing yourself or adopt a toolchain that has.