Psalm

LanguagePHP
Kindtool
Readiness🟡
Detector conformance🟡
Checked2026-09-08, version 6.16.1

Psalm is a static analyser for PHP with a strong type system and a plugin API. In a pipeline it plays the same role as PHPStan, and many projects run both; for this method it is a detector that can host a bespoke rule through a plugin.

How it is conformant

Clause 4.1 is met: a plugin hooks into analysis events and can emit an issue of its own by extending Psalm\Issue\PluginIssue (authoring plugins). Clause 4.3 is met in part, because a plugin issue carries a name the author chooses, that name is what @psalm-suppress and <PluginIssue name="..."> key on, and it is printed as the issue type with every finding. Clauses 5.1 to 5.4 hold: vendor/bin/psalm file1.php runs locally on a named file and prints its findings to the terminal, with --report adding a file rather than replacing the output (command line usage). Clause 6.1 is met for bundled issues, whose catalogue on psalm.dev is keyed on the printed issue name, which is the URL form the clause accepts. Clause 7.1 is met for the baseline, which is active only whilst the configuration names an errorBaseline file (dealing with code issues).

How it is not conformant

Clause 4.2 is the readiness gap and a failing MUST: Psalm ships no harness that runs one plugin issue over one fixture. The ecosystem’s answer is the third-party Codeception module used by the plugin skeleton and by published plugins (codeception-psalm-module on Packagist), which is a workaround rather than a route the detector provides. Clause 4.3 is only partly met because the issue name has no separate identifier field and is tied to the issue class, so a rename of the class renames the printed type. Clauses 6.2 and 6.3 fail: the issue catalogue is a website, nothing installed resolves a printed name, and the documentation is not tracked at the installed version. Clause 7.1 is only partly met for the inline route, since @psalm-suppress IssueName and @psalm-suppress all have no documented switch and Psalm documents no check that finds them, though a plugin hooking file analysis can read the source. Clause 7.2 fails because neither the annotation nor an issue handler has a field for a reason. Clause 4.4 is not met and clause 6.4 was not verified.

Clause by clause

Document Clause Result Evidence
Detector 4.1 Yes Plugins emit PluginIssue subclasses, authoring plugins
Detector 4.2 Partial Only through a third-party Codeception module, Packagist
Detector 4.3 Partial Issue name is author-chosen and printed as the type; no separate identifier field, so it is tied to the class
Detector 4.4 No Nothing enforces an identifier on a plugin issue
Detector 5.1 Yes vendor/bin/psalm runs locally, command line usage
Detector 5.2 Yes File arguments accepted, command line usage
Detector 5.3 Yes Findings print to the terminal; --report adds a file
Detector 5.4 Yes No CI-only mode
Detector 6.1 Yes Built-in issues documented online keyed on the printed name; a plugin issue owes only the unaltered name
Detector 6.2 No Documentation is on psalm.dev, not installed
Detector 6.3 No Same
Detector 6.4 Not verified No release gate over issue documentation was found
Detector 7.1 Partial Baseline is opt-in by configuration; @psalm-suppress has no switch or documented check, dealing with code issues
Detector 7.2 No No reason field anywhere, dealing with code issues

Notes for a practitioner

Write the defence as a plugin issue with a name you will never change, prove it with the Codeception Psalm module and a fixture, and sweep with a single-file run. Forbid @psalm-suppress in your own coding standard and keep every exception in <issueHandlers> with a comment beside it, because Psalm will not ask for one.