A missing type declaration in a service constructor, a tainted $_GET parameter
flowing straight into a query string, a class upgraded to PHP 8.4 with deprecated
call-by-reference semantics still lurking three levels down — these are the bugs that
survive code review and land in production PHP applications. PHP code analysis
catches them before the code ships. This guide covers every major tool in the 2026 PHP
ecosystem — PHPStan, Psalm, PHPCS, Rector, and four more — and shows you how to layer
them into a workflow that finds real bugs without drowning your team in noise. If you
work in other languages, the same layering principles apply to
Python static code analysis and
Java static analysis.
What Is PHP Code Analysis?
PHP code analysis is the automated inspection of PHP source files
without executing them — a practice known formally as
static program analysis.
A PHP analyzer reads your .php files, constructs an
abstract syntax tree (AST) from the parsed tokens, and then applies a configurable
rule set to that tree. Findings — type mismatches, undefined variables, unreachable
code, coding-standard violations, security sinks — are reported back to the developer
in the terminal or CI pipeline, without a web server, database, or runtime being
involved at any point.
The term is an umbrella over several distinct analysis categories, each requiring different tooling:
- Type analysis — verifying that method arguments, return types, and property assignments are consistent with declared type annotations and inferred types. Handled by PHPStan and Psalm.
- Coding standards enforcement — checking PSR-12 formatting, brace placement, naming conventions, and project-specific style rules. Handled by PHP_CodeSniffer (PHPCS).
- Auto-formatting — deterministically rewriting code to match a defined style, eliminating style debates in reviews. Handled by PHP-CS-Fixer.
- Automated refactoring — rewriting code to use newer language features or upgrade framework APIs. Handled by Rector.
- Heuristic checks — cyclomatic complexity, dead code, overly long methods, naming conventions. Handled by PHPMD.
- Taint analysis (SAST) — tracking user-supplied data from HTTP input through the call graph to dangerous sinks (database queries, HTML output, file writes). Handled by Psalm with taint mode and tools covered in our SAST tools guide.
Because none of these tools execute your application, they are safe to run on partially written code, fast enough for pre-commit hooks, and trivially embeddable in GitHub Actions — forming the static layer of a complete code quality pipeline. The human-review layer that sits on top of static analysis is covered in our guide to source code review tools.
Why PHP Static Analysis Matters in 2026
PHP's permissive dynamic typing has historically made entire categories of bugs invisible until runtime. The language has evolved dramatically — PHP 8.4 brings property hooks, asymmetric visibility, and a strengthened type system — but legacy codebases running PHP 7.x still exist in production, and even modern PHP applications can hide type-level bugs that no amount of unit testing will surface if the test suite does not cover every code path. Three trends make 2026 the right time to invest in a proper PHP code analysis stack:
- Type coverage is now practical. PHPStan and Psalm both support generics, conditional return types, template annotations, and PHP 8.x union types. A codebase with reasonable docblock coverage can run PHPStan at level 6 or higher and catch entire classes of null-dereference and argument-type bugs that no linter or test suite can see.
- Automated upgrades are real. Rector can rewrite a PHP 7.4 codebase to PHP 8.4 automatically — replacing deprecated constructs, adding union types, converting create_function calls, and updating framework API calls. What previously required weeks of manual migration now takes hours, with a static analysis pass afterward to verify correctness.
- Security shift-left is expected. PHP applications remain high-value targets for SQL injection and XSS. Running Psalm with taint analysis pre-merge catches tainted-data flows that no linter detects. For teams treating security as a first-class concern, the full static code analysis stack is now a minimum bar, not a nice-to-have.
The 8 Best PHP Code Analysis Tools
1. PHPStan — The Modern Default
PHPStan is the most widely adopted PHP code analysis tool in 2026, and for good reason. Its defining feature is an eleven-level strictness ladder (0 through 10, with 0 being the most permissive and 10 the most strict) that lets teams adopt type analysis incrementally. A legacy codebase can start at level 0 with zero findings, progressively tighten the level over successive sprints, and eventually reach level 6 or higher — where genuine type-safety bugs surface — without being paralyzed by thousands of pre-existing issues on day one.
PHPStan performs bleeding-edge type inference: it understands generics via PHPDoc
annotations, conditional return types, intersection types, and PHP 8.4 property hooks.
It ships with a growing plugin ecosystem — most notably
Larastan, the Laravel-specific extension that teaches PHPStan about
Eloquent magic methods, relationship return types, and Blade templates. There are
equivalent extensions for Symfony (phpstan-symfony) and Doctrine
(phpstan-doctrine). Maintained by Ondrej Mirtes under MIT license.
composer require --dev phpstan/phpstan
# Run at level 6 on your src directory
./vendor/bin/phpstan analyse src/ --level=6 <?php
// PHPStan level 6 catches this at analysis time:
class OrderService {
public function getTotal(Order $order): float {
// Error: Property Order::$items might not be defined.
return array_sum($order->items);
}
} 2. Psalm — Vimeo's Security-First Analyzer
Psalm was developed by Vimeo and is the only major PHP analyzer with built-in taint analysis — the ability to track user-controlled data from its source (HTTP request parameters, cookies, file uploads) through the call graph to dangerous sinks (database queries, HTML output, system calls). This makes Psalm uniquely valuable for security-conscious teams: it can detect SQL injection and XSS vulnerabilities that PHPStan's type-only analysis completely misses.
Beyond security, Psalm offers granular @psalm-* annotation support —
@psalm-pure, @psalm-immutable, @psalm-template
— that lets teams encode rich invariants directly in docblocks. Psalm is slower
than PHPStan on large codebases, but its error-level system (1 through 8) provides
a comparable incremental adoption path. Maintained by the open-source community
under MIT license.
composer require --dev vimeo/psalm
./vendor/bin/psalm --init src/ 4 # initialise at error level 4 <?php
// Psalm taint analysis catches this:
function showUser(PDO $db): void {
/** @psalm-taint-source input */
$id = $_GET['id'];
// Psalm: TaintedSql — $id flows from HTTP input to query string
$db->query("SELECT * FROM users WHERE id = $id");
} 3. PHP_CodeSniffer (PHPCS) — Coding Standards Enforcement
PHP_CodeSniffer is the de-facto tool for enforcing coding standards in PHP projects.
It ships with built-in rulesets for PSR-1, PSR-2, PSR-12, WordPress, Squiz, and
Zend, and teams can write custom XML sniff rules. Importantly, PHPCS is a style-only
tool — it checks indentation, brace placement, spacing, naming conventions, and line
length, but it does not catch logic bugs or type errors. Think of it as the PHP
equivalent of Flake8's style-enforcement mode. The companion
phpcbf binary auto-fixes many violations. Maintained by
Squiz Labs under BSD-3-Clause.
composer require --dev squizlabs/php_codesniffer
./vendor/bin/phpcs src/ --standard=PSR12 <?php
// PHPCS PSR-12 flags this:
class foo { // Error: Class name "foo" is not in PascalCase
function Bar (){ // Error: Opening brace must be on next line
$x=1; // Error: No spaces around assignment operator
}
} 4. PHP-CS-Fixer — Auto-Formatter
PHP-CS-Fixer is PHP's closest equivalent to Black (Python) or Prettier (JavaScript). Rather than just reporting violations, it rewrites your source files to conform to hundreds of configurable rules — covering everything from trailing commas in function calls to yoda conditions to multiline string formatting. The tool is idempotent: running it twice produces the same output, which makes it safe to add to pre-commit hooks without risk of infinite loops. PHP-CS-Fixer and PHPCS complement each other: run PHP-CS-Fixer first to auto-apply fixes, then run PHPCS to catch any remaining issues the fixer did not address. Maintained by the FriendsOfPHP organization under MIT license.
composer require --dev friendsofphp/php-cs-fixer
./vendor/bin/php-cs-fixer fix src/ --rules=@PSR12 5. Rector — AST-Based Automated Refactoring
Rector occupies a unique category: it is not a linter or a formatter, but an
automated code rewriter. Rector parses PHP source into an AST, applies a configurable
set of transformation rules, and writes the modified source back to disk. Its most
compelling use case is automated PHP version upgrades — Rector can migrate a PHP 7.4
codebase to PHP 8.4 by automatically converting nullable type hints, adding match
expressions, replacing strpos null checks with str_contains,
converting create_function closures, and adding readonly property
declarations where safe.
Rector pairs naturally with PHPStan: a common workflow is to run PHPStan to establish a pre-migration type-safety baseline, run Rector to apply the automated upgrade, then re-run PHPStan to verify the refactored code is still type-safe. Maintained by Tomas Votruba under MIT license.
composer require --dev rector/rector
# Preview changes without writing (dry-run)
./vendor/bin/rector process src/ --dry-run <?php
// Rector upgrades this PHP 7.4 pattern automatically:
// BEFORE:
function getUser(?int $id): ?User {
if ($id === null) { return null; }
return strpos($id, 0) !== false ? find($id) : null;
}
// AFTER (PHP 8.x):
function getUser(?int $id): ?User {
if ($id === null) { return null; }
return str_contains((string)$id, '0') ? find($id) : null;
} 6. PHPMD — Heuristic Complexity Analysis
PHP Mess Detector (PHPMD) takes a different approach from PHPStan and Psalm: rather than type inference, it applies heuristic rules to detect code quality issues — excessive cyclomatic complexity, overly long classes and methods, deeply nested control structures, dead code (unused variables, parameters, and methods), and naming convention violations. PHPMD predates the modern PHP analysis era and is slower to evolve than PHPStan, but it remains useful for enforcing complexity budgets (for example, a maximum cyclomatic complexity of 10 per method) that type analyzers do not address. Maintained under BSD-3-Clause.
composer require --dev phpmd/phpmd
./vendor/bin/phpmd src/ text cleancode,codesize,unusedcode <?php
// PHPMD flags this for excessive complexity (CyclomaticComplexity > 10):
class OrderProcessor {
public function process(Order $o): string {
if ($o->type === 'A') {
if ($o->status === 'pending') {
if ($o->amount > 100) {
// ... 8 more nested conditions
}
}
}
return 'done';
}
} 7. Phan — Etsy's Incremental Analyzer
Phan was originally developed at Etsy and remains notable for two features: minimal
runtime dependencies (it uses the php-ast native extension for parsing,
which is dramatically faster than a pure-PHP parser) and genuine incremental analysis
(only re-analyzing files that changed since the last run, with results cached in a
SQLite database). For large codebases where PHPStan's full re-analysis is too slow,
Phan's incremental mode can provide meaningful speedups in the edit-analyze loop.
In practice, Phan has lost significant ground to PHPStan and Psalm in terms of rule
quality, ecosystem, and active development. It is worth evaluating for very large
monorepos where speed is the binding constraint. Maintained under Apache-2.0.
composer require --dev phan/phan
./vendor/bin/phan --allow-polyfill-parser -k .phan/config.php 8. Framework Extensions: Larastan, phpstan-symfony, and Friends
PHPStan's extension ecosystem deserves its own mention. Framework-specific PHPStan
extensions teach the analyzer about magic methods, dynamic properties, and container
bindings that raw PHP type inference cannot resolve. Larastan
(maintained by Nuno Maduro, MIT) handles Eloquent relationships, model properties,
facades, and helper functions in Laravel — without it, PHPStan would generate
hundreds of false positives on any Laravel project. phpstan-symfony
understands Symfony service containers and form types.
phpstan-doctrine resolves Doctrine entity associations and
repository return types. These are not separate tools; install them alongside
PHPStan and reference them in phpstan.neon. They are what makes
PHPStan practically viable for framework-based projects.
# Laravel + Larastan setup
composer require --dev nunomaduro/larastan
# phpstan.neon
# includes:
# - ./vendor/nunomaduro/larastan/extension.neon
# parameters:
# level: 6
# paths: [app/] Comparison Table: PHP Code Analysis Tools at a Glance
| Tool | Type | License | Best for | PHP 8.4 support | Speed | Learning curve |
|---|---|---|---|---|---|---|
| PHPStan | Type analyzer | MIT | Default type-safety gate; Laravel/Symfony projects | Yes | Fast | Low–Medium |
| Psalm | Type analyzer + SAST | MIT | Security-sensitive apps; taint tracking | Yes | Moderate | Medium |
| PHPCS | Style linter | BSD-3 | PSR-12 / WordPress / Symfony coding standards | Yes | Fast | Low |
| PHP-CS-Fixer | Auto-formatter | MIT | Auto-apply formatting; pre-commit hook | Yes | Fast | Low |
| Rector | AST refactoring | MIT | PHP version upgrades; framework migrations | Yes | Moderate | Medium |
| PHPMD | Heuristic linter | BSD-3 | Complexity budgets; dead code; naming | Partial | Moderate | Low |
| Phan | Type analyzer | Apache-2.0 | Large monorepos needing incremental analysis | Partial | Fast (incremental) | High |
| Larastan | PHPStan extension | MIT | Laravel Eloquent, facades, helpers | Yes | Fast | Low |
PHPStan vs. Psalm: Which Should You Pick?
PHPStan and Psalm are both mature, actively maintained, MIT-licensed type analyzers for PHP. Both support generics via PHPDoc annotations. Both understand PHP 8.x union types, intersection types, and named arguments. Both have plugin architectures. The differences come down to three axes: speed, ecosystem breadth, and security depth.
PHPStan's Advantages
PHPStan is faster on large codebases — its analysis engine is more aggressively
optimized and its result cache more effective at incremental re-analysis. More
critically, PHPStan's plugin ecosystem is significantly broader. Larastan alone has
over 5,000 GitHub stars and is the de-facto standard for PHP code analysis
tools in Laravel projects. phpstan-symfony,
phpstan-doctrine, phpstan-phpunit, and a dozen other
framework-specific extensions handle the magic methods and dynamic patterns that
make raw type inference produce false positives on framework code. PHPStan's
strictness ladder (levels 0–10) is also better documented and more predictable
than Psalm's error-level system, making it easier to set team-wide thresholds and
enforce them in CI.
Psalm's Advantages
Psalm's killer feature is taint analysis. Enable it with
--taint-analysis and Psalm traces the path of user-controlled data —
$_GET, $_POST, $_COOKIE, database reads
marked as tainted — through your entire call graph, flagging anywhere that data
reaches a dangerous sink without sanitization. This catches SQL injection,
stored XSS, and path traversal patterns that PHPStan's type-only analysis completely
misses. Psalm also offers more granular annotation support:
@psalm-pure enforces referential transparency, and
@psalm-immutable prevents mutation of value objects — annotations that
encode architectural invariants directly in the codebase.
Recommendation
Use PHPStan as the default for the vast majority of projects. Start at level 5 or 6, add the relevant framework extension, and enforce it in CI on every pull request. Add Psalm — specifically its taint analysis mode — only when your threat model genuinely requires systematic SQL injection and XSS tracking across the full call graph. Running both tools doubles your CI analysis time without proportional benefit unless taint tracking is a specific requirement. For broader context on SAST tooling choices, see our SAST tools comparison.
Setting Up a Modern PHP Code Analysis Workflow
A well-structured 2026 PHP code analysis workflow has four layers, each running at a different frequency. The goal is to keep the fast layers (pre-commit and local) under five seconds while reserving deeper checks for CI and scheduled runs.
- Pre-commit (fast): PHP-CS-Fixer + PHPCS. Auto-format first, then check style. Both run in under two seconds on a typical changed-file set.
- Local + CI (medium): PHPStan at level 6 or higher. Run locally on save (if your editor supports it) and as a required CI gate on every pull request.
- CI nightly (deep): Psalm with
--taint-analysis. Slower but catches security issues; run on the main branch nightly rather than on every commit. - Quarterly (upgrades): Rector. Schedule a periodic run to apply the latest PHP version upgrade rules and keep the codebase current with the language.
Step 1: Install the Tools
composer require --dev \
phpstan/phpstan \
vimeo/psalm \
squizlabs/php_codesniffer \
friendsofphp/php-cs-fixer \
rector/rector Step 2: Configure Pre-commit Hooks
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: php-cs-fixer
name: PHP CS Fixer
entry: ./vendor/bin/php-cs-fixer fix
language: script
types: [php]
args: [--rules=@PSR12]
- id: phpcs
name: PHP CodeSniffer
entry: ./vendor/bin/phpcs
language: script
types: [php]
args: [--standard=PSR12] Step 3: Add a GitHub Actions CI Pipeline
# .github/workflows/quality.yml
name: PHP Code Analysis
on: [push, pull_request]
jobs:
analyse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- name: Install dependencies
run: composer install --no-progress --prefer-dist
- name: PHP CS Fixer (check only)
run: ./vendor/bin/php-cs-fixer fix --dry-run --diff src/
- name: PHPCS
run: ./vendor/bin/phpcs src/ --standard=PSR12
- name: PHPStan
run: ./vendor/bin/phpstan analyse src/ --level=6 --no-progress Step 4: Add Psalm Nightly for Taint Analysis
# In your workflow, add a scheduled job:
psalm-nightly:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4' }
- run: composer install --no-progress --prefer-dist
- name: Psalm taint analysis
run: ./vendor/bin/psalm --taint-analysis Why Visual Diff Review Belongs Before Static Analysis
Static analysis tools catch type errors, style violations, and dangerous data flows.
Automated tests catch behavioral regressions. But there is a third category of bug
that neither tool class reliably catches: intent mismatches — changes where
the code is syntactically correct, type-safe, and passes all tests, but does something
subtly different from what the developer intended. Copy-paste errors with
near-identical variable names. Business rule conditions with a swapped
> and >=. A refactored function that accidentally
drops one clause of a compound condition. These bugs are invisible to analyzers
because they represent correct code that encodes the wrong logic.
Visual diff review — comparing the exact lines that changed before running any automated tool — catches this category of bug by forcing a human to read the delta. When a reviewer can see precisely which lines were added, removed, or modified, they can reason about the intent of the change independently of whether the code compiles or passes analysis. The three layers complement each other: human diff review catches intent bugs, static analysis catches type and security bugs, tests catch behavioral regressions. Skipping the human-review layer means that intent bugs — which tend to be high-severity and slow to diagnose after deployment — survive all automated gates.
Diff Checker is a free Chrome extension (Manifest V3, version 1.1.11) that brings Monaco-based syntax highlighting — the same engine that powers VS Code — to PHP code review directly in the browser. It supports split view (side-by-side) and unified view, toggleable per session. Three diff algorithms are available: Smart Diff, Ignore Whitespace, and Classic (LCS), covering scenarios from whitespace-heavy reformats to algorithm comparisons. Unchanged regions collapse automatically with 0–5 lines of context, keeping long files navigable. One-click revert per change block lets reviewers restore individual sections without affecting surrounding changes.
The Normalize button handles a common PHP review friction point: auto-formatted files where PHP-CS-Fixer or PHPCS has changed whitespace throughout. Normalize strips incidental whitespace differences, leaving only substantive logic changes visible. The full diff computation is client-side — your PHP source never leaves the browser unless you explicitly opt into the AI Summary feature (which requires a user-supplied OpenAI key and is disabled by default). This matters for commercial PHP codebases where IP confidentiality is a concern.
The recommended workflow for PHP code review:
- Open Diff Checker and paste the before/after versions of the changed file.
- Review the highlighted changes to understand the logical intent of the diff.
- Run PHPStan and PHPCS in CI.
- Map each static analysis finding back to the specific changed lines you reviewed.
- Dismiss pre-existing findings; fix issues introduced by the current change.
This diff-first approach is especially effective for PHP refactors involving Rector or automated formatting passes, where a large number of lines change simultaneously. Being able to toggle between Smart Diff and Ignore Whitespace modes lets reviewers first confirm that the functional changes are correct, then verify that no unintended structural changes crept in. For teams comparing JSON configuration files (a common source of environment drift in PHP deployments), Diff Checker's key-sorting normalization also helps surface meaningful differences buried under key-order variations.
Frequently Asked Questions
PHPStan vs Psalm — which should I use?
PHPStan is the right default for most projects. It is faster, has a broader extension
ecosystem (Larastan for Laravel, phpstan-symfony, phpstan-doctrine), and its
eleven-level strictness ladder (0–10) makes incremental adoption straightforward. Psalm is the
right choice when taint analysis is a specific requirement — it tracks
$_GET, $_POST, and other tainted sources through the full
call graph to dangerous sinks, catching SQL injection and XSS patterns that
PHPStan's type-only analysis cannot see. Start with PHPStan at level 6; add Psalm
only if your application handles sensitive user data and you need systematic taint
tracking as a security gate.
Can I use PHPCS and PHP-CS-Fixer together?
Yes. They serve different roles. PHP-CS-Fixer is a formatter — it rewrites your files to match a defined style, auto-applying hundreds of rules without requiring manual fixes. PHPCS is a reporter — it detects violations against a named ruleset and fails the build if any remain. Run PHP-CS-Fixer first to auto-apply all fixes, then run PHPCS to catch any violations the fixer does not cover. In pre-commit hooks, PHP-CS-Fixer runs in fix mode; in CI, PHPCS runs in check-only mode and fails the build on remaining violations. The combination gives you both automated fixing and a hard gate — without duplicate effort between the two tools.
What does Rector actually do?
Rector parses your PHP source into an AST, applies a set of transformation rules,
and writes the modified code back to disk. It does not report issues — it fixes them
automatically. The most common use case is automated PHP version upgrades: Rector
can migrate a PHP 7.4 codebase to PHP 8.4 by adding union types, converting
create_function closures to arrow functions, replacing null-check
patterns with str_contains or the null-safe operator, and adding
readonly declarations where safe. Run Rector with --dry-run first to
preview changes, then apply and re-run PHPStan to verify the refactored code is
still type-safe.
How do I integrate PHP static analysis into GitHub Actions?
Create a .github/workflows/quality.yml file that triggers on
push and pull_request. Use the
shivammathur/setup-php action to install PHP 8.4, run
composer install, then add separate steps for PHP-CS-Fixer in
--dry-run mode, PHPCS with your standard, and PHPStan at your chosen
level. Each step exits non-zero on violations, so the workflow fails and blocks
merging. The complete YAML configuration in the workflow section above can be copied
directly into your repository. For teams using the tools covered in our broader
static code analysis tools guide,
the same layered approach applies regardless of language.
Are PHP code analysis tools free?
Yes. Every tool in this guide is free and open-source: PHPStan (MIT), Psalm (MIT), PHP_CodeSniffer (BSD-3), PHP-CS-Fixer (MIT), Rector (MIT), PHPMD (BSD-3), Phan (Apache-2.0), and Larastan (MIT). There are no SaaS subscriptions, usage limits, or cloud accounts required. The complete PHP code analysis tools stack — including CI integration — runs entirely on your own infrastructure with zero license cost, making it accessible to projects of every size from solo indie projects to large engineering teams.