PHPCS + PHPStan in CI (Workflow Guide)
debt(d7/e5/b3/t5)
Closest to 'only careful code review or runtime testing' (d7) — the absence of CI enforcement for phpcs/phpstan isn't caught by any tool automatically; it requires someone to inspect the CI config and notice quality gates are missing.
Closest to 'touches multiple files / significant refactor in one component' (e5) — quick_fix says configure phpcs and phpstan in composer scripts and CI, which means adding CI workflow files, composer.json scripts, phpstan.neon, phpcs.xml, and likely a baseline for legacy code; more than a one-line patch.
Closest to 'localised tax' (b3) — the workflow config lives in CI/composer files and imposes a modest ongoing tax (devs must fix violations, maintain baselines) but doesn't shape the architecture of the application code itself.
Closest to 'notable trap (a documented gotcha)' (t5) — the misconception that local runs suffice is a well-known gotcha; also PHPStan-at-level-0-forever and baseline misuse are documented traps most teams eventually learn.
Also Known As
TL;DR
Explanation
PHP_CodeSniffer (phpcs) enforces formatting rules — PSR-12 compliance, trailing whitespace, line length, brace placement. PHPStan catches logic and type errors that formatting tools miss — wrong return types, null dereferences, dead code. Run them as separate CI steps so failures are clearly attributed. Recommended CI pipeline: composer install → phpcs --standard=PSR12 src/ → phpstan analyse src/ --level=6 → phpunit. Use phpcbf (the fixer companion to phpcs) as a pre-commit hook so formatting issues never reach CI. Store PHPStan configuration in phpstan.neon, baseline in phpstan-baseline.neon for legacy code. Run both in parallel where CI supports it. Add psalm as a third layer for teams wanting maximum type safety coverage.
Common Misconception
Why It Matters
Common Mistakes
- Running tools only locally — developers skip them under pressure; CI enforces them for everyone.
- Starting PHPStan at level 0 and never increasing — it provides minimal value at the lowest level.
- Not using a baseline for legacy code — a fresh PHPStan run on old code produces thousands of errors; baseline incrementally.
- Fixing PHPCS warnings by suppressing them rather than correcting the code.
Code Examples
# CI pipeline without static analysis:
steps:
- run: composer test # Tests only — no style or type checking
# No phpcs, no phpstan
# Type errors and style violations merge undetected
# With quality gates:
steps:
- run: vendor/bin/phpcs --standard=PSR12 src/
- run: vendor/bin/phpstan analyse --level=8 src/
- run: composer test
# CI workflow — code quality gates
# phpcs.xml
<?xml version="1.0"?>
<ruleset name="Project">
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<rule ref="PSR12"/>
<exclude-pattern>vendor/*</exclude-pattern>
<exclude-pattern>database/migrations/*</exclude-pattern>
</ruleset>
# phpstan.neon
parameters:
level: 6
paths:
- src
- tests
ignoreErrors:
- '#Call to an undefined method Illuminate#'
# composer.json scripts
"ci": ["@lint", "@analyse", "@test", "@audit"]
"lint": "phpcs"
"analyse": "phpstan analyse"
# .github/workflows/ci.yml
- run: composer lint
- run: composer analyse
- run: composer test