Static Analysis
debt(d7/e3/b5/t5)
Closest to 'only careful code review or runtime testing' (d7). The absence of static analysis in a CI pipeline is not caught by any compiler or default linter — it requires someone to notice the pipeline configuration is missing the tool. The detection_hints note that type errors are 'only discovered at runtime' when PHPStan/Psalm are absent, and the gap is invisible until bugs surface in production. Slightly better than d9 because a CI audit or onboarding review can surface it.
Closest to 'simple parameterised fix' (e3). The quick_fix is to add PHPStan at level 6+ to CI and incrementally raise the level using a baseline file. This is more than a one-line patch (requires CI config changes, possibly a phpstan.neon, and fixing or baselining existing errors) but is contained within one component (the CI/tooling layer). It does not require changes across multiple application files unless errors are fixed rather than baselined.
Closest to 'persistent productivity tax' (b5). Applies across all three contexts (web, cli, queue-worker) and affects every developer and every PR in the project. Running at level 0 or suppressing warnings creates a persistent tax — developers must work around false confidence, or spend time fighting suppressed warnings. However it does not fundamentally reshape architecture (not b7), so b5 is appropriate.
Closest to 'notable trap — a documented gotcha most devs eventually learn' (t5). The misconception field is explicit: developers believe static analysis only catches style/smells, but modern PHPStan/Psalm perform full type inference and catch logic errors. This is a well-documented gotcha that competent developers who haven't used modern PHP static analysers will commonly hold, leading them to run at low levels or skip CI integration entirely.
Also Known As
TL;DR
Explanation
PHP static analysis tools include PHPStan (type inference and level-based strictness), Psalm (advanced type system, taint analysis for security), PHP_CodeSniffer (coding standards), PHP-CS-Fixer (auto-corrects style), and PHPMD (mess detector). PHPStan/Psalm can find type errors, undefined variables, null dereferences, and incorrect function signatures without running code. Taint analysis in Psalm traces user-supplied data through the application to identify injection sinks. Integrate static analysis in CI at the highest level the codebase tolerates, incrementally increasing strictness.
Diagram
flowchart LR
CODE[PHP Source] --> PHPSTAN[PHPStan]
CODE --> PSALM[Psalm]
subgraph What_They_Check
TYPES[Type errors<br/>wrong parameter types]
NULL[Null pointer access<br/>nullable not handled]
DEAD[Dead code<br/>unreachable statements]
UNDEF[Undefined variables<br/>methods properties]
end
PHPSTAN & PSALM --> TYPES & NULL & DEAD & UNDEF
subgraph Levels
L0[Level 0 - basic]
L5[Level 5 - medium]
L9[Level 9 - strict max]
L0 --> L5 --> L9
end
style PHPSTAN fill:#1f6feb,color:#fff
style PSALM fill:#6e40c9,color:#fff
style L9 fill:#238636,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Running PHPStan/Psalm at level 0 and thinking you have static analysis — max level catches the most issues.
- Not running static analysis in CI — developers disable it locally and analysis never runs.
- Ignoring analysis warnings by suppressing them rather than fixing the underlying issue.
- Not using baseline files for legacy codebases — running analysis for the first time produces thousands of errors; baseline lets you fix incrementally.
Code Examples
// PHPStan finds type error static analysis catches before runtime:
function getUser(int $id): User {
$result = $this->db->find($id); // Returns User|null
return $result; // PHPStan error: null not assignable to User
// Fix: add null check or change return type to ?User
}
// Static analysis — finds bugs without running code
// PHPStan — type checking, dead code, undefined variables
$ vendor/bin/phpstan analyse src/ --level=6
// Level 0 = basic | Level 9 = strictest
// Psalm — type inference + taint analysis
$ vendor/bin/psalm
$ vendor/bin/psalm --taint-analysis // tracks user input to dangerous sinks
// PHP_CodeSniffer — coding standards
$ vendor/bin/phpcs --standard=PSR12 src/
// PHPMess Detector — complexity, unused code
$ vendor/bin/phpmd src/ text cleancode,codesize,controversial,design
// Integrate all into CI:
// Each runs on every PR — failures block merge
// PHPStan baseline — acknowledge existing issues, block new ones:
$ vendor/bin/phpstan --generate-baseline