code quality and tooling
Local Quality Gates for PHP Examples
A local quality gate is a set of checks you run before asking other people or CI to review your code. In PHP projects, this usually includes syntax checks, formatting checks, static analysis, and tests.
The purpose is not to make development slower. The purpose is to catch obvious problems locally while they are cheap to fix.
What a quality gate checks
A practical PHP quality gate might include:
php -lto catch syntax errors- PHP-CS-Fixer or PHP_CodeSniffer to check style
- PHPStan or Psalm to check types and structure
- PHPUnit to check behaviour
- Rector dry-run when the project uses automated refactoring
Not every project uses every tool. Follow the repository.
Composer scripts make checks repeatable
Instead of asking every developer to remember long commands, put them in composer.json.
{
"scripts": {
"lint": "find src tests -name '*.php' -print0 | xargs -0 -n1 php -l",
"format:check": "php-cs-fixer fix --dry-run --diff",
"analyse": "phpstan analyse",
"test": "phpunit",
"check": [
"@lint",
"@format:check",
"@analyse",
"@test"
]
}
}
Then the local command is simple:
composer check
Use the scripts already present in the project. If the project has no scripts, adding them can be a useful improvement.
Syntax checks are fast
php -l checks whether a file has valid PHP syntax. It does not run the program.
php -l src/OrderTotal.php
Syntax checks catch missing braces, invalid tokens, and parse errors. They do not prove business logic.
Formatting checks prevent noisy reviews
Run the formatter in check mode before a pull request.
vendor/bin/php-cs-fixer fix --dry-run --diff
or:
vendor/bin/phpcs
The exact command depends on the formatter.
Static analysis catches type problems
Run the project's static analyser.
vendor/bin/phpstan analyse
or:
vendor/bin/psalm
Static analysis is especially useful for catching wrong return types, missing array keys, nullable values used unsafely, and incorrect PHPDoc.
Tests prove behaviour
Tests answer a different question from formatting and static analysis. They check that the code behaves as expected.
vendor/bin/phpunit
For a small bug fix, a focused test around the changed rule is often better than only clicking around manually.
Run checks before pushing
A useful workflow:
git status --short
composer check
git diff
Run checks, then review the diff. If a formatter changes files, inspect that diff before committing.
Do not hide failures
If a check fails, read the first failure carefully. Fix the root cause. Avoid weakening the check just to make the command pass.
Bad responses include:
- ignoring all static analysis errors globally
- removing a test because it fails
- committing generated files to satisfy a missing path
- suppressing a warning without understanding it
- skipping the formatter because the diff is inconvenient
What to remember
A local quality gate is the developer's first line of defence. It should be easy to run, repeatable, and close to the same checks CI will run.
Before moving on, make sure you can describe what composer check should do in a PHP project and why formatting, static analysis, and tests catch different problems.
Practice
Task: Build A Composer Quality Gate
Create Composer scripts for a small PHP project quality gate.
Requirements
- Add a
format:checkscript using PHP-CS-Fixer dry-run with diff. - Add an
analysescript using PHPStan. - Add a
testscript using PHPUnit. - Add a
checkscript that runs all three. - Explain why
checkshould be run before opening a pull request.
Show solution
{
"scripts": {
"format:check": "php-cs-fixer fix --dry-run --diff",
"analyse": "phpstan analyse",
"test": "phpunit",
"check": [
"@format:check",
"@analyse",
"@test"
]
}
}
Run the full local gate with:
composer check
composer check should be run before opening a pull request because it catches formatting, static-analysis, and behaviour failures before reviewers or CI spend time on them.