08: Code Quality - ESLint meets PHP_CodeSniffer
Code Quality: ESLint meets PHP_CodeSniffer
Section titled “Code Quality: ESLint meets PHP_CodeSniffer”Overview
Section titled “Overview”The PHP ecosystem has a rich set of code quality tools similar to TypeScript’s ESLint, Prettier, and TypeScript compiler. This chapter maps your existing knowledge to PHP’s tooling landscape.
Learning Objectives
Section titled “Learning Objectives”By the end of this chapter, you’ll be able to:
- ✅ Use PHP_CodeSniffer (PHPCS) for code style checking
- ✅ Apply PHPStan for static analysis (like TypeScript compiler)
- ✅ Format code with PHP-CS-Fixer (like Prettier)
- ✅ Configure quality tools for your project
- ✅ Integrate tools with your editor
- ✅ Set up pre-commit hooks
- ✅ Run quality checks in CI/CD
Code Examples
Section titled “Code Examples”📁 View Code Examples on GitHub
This chapter includes code quality tool examples:
- PHP_CodeSniffer configuration
- PHPStan/Psalm setup
- PHP-CS-Fixer configuration
- Quality check scripts
Setup the tools:
cd code/php-typescript-developers/chapter-08composer installcomposer checkTool Ecosystem Comparison
Section titled “Tool Ecosystem Comparison”| TypeScript/JavaScript | PHP | Purpose |
|---|---|---|
| ESLint | PHP_CodeSniffer (PHPCS) | Code style linting |
| Prettier | PHP-CS-Fixer / Laravel Pint | Code formatting |
| TypeScript Compiler | PHPStan / Psalm | Static analysis |
| SonarQube | SonarQube (same) | Code quality platform |
| Husky | GrumPHP / Husky | Git hooks |
Code Style: ESLint vs PHP_CodeSniffer
Section titled “Code Style: ESLint vs PHP_CodeSniffer”ESLint Setup
Section titled “ESLint Setup”npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin.eslintrc.json:
{ "parser": "@typescript-eslint/parser", "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "semi": ["error", "always"], "quotes": ["error", "single"], "indent": ["error", 2], "no-unused-vars": "error", "@typescript-eslint/no-explicit-any": "warn" }}Run:
npx eslint src/npx eslint src/ --fixPHP_CodeSniffer Setup
Section titled “PHP_CodeSniffer Setup”composer require --dev squizlabs/php_codesnifferphpcs.xml:
<?xml version="1.0"?><ruleset name="MyProject"> <description>Coding standard for my project</description>
<!-- Check all PHP files in src/ --> <file>src</file>
<!-- Use PSR-12 standard --> <rule ref="PSR12"/>
<!-- Custom rules --> <rule ref="Generic.Files.LineLength"> <properties> <property name="lineLimit" value="120"/> <property name="absoluteLineLimit" value="150"/> </properties> </rule>
<!-- Ignore specific patterns --> <exclude-pattern>*/vendor/*</exclude-pattern></ruleset>Run:
vendor/bin/phpcsvendor/bin/phpcbf # Automatic fix (like --fix)Coding Standards:
- PSR-1: Basic coding standard
- PSR-12: Extended coding style (successor to PSR-2)
- PEAR: PEAR coding standard
- Zend: Zend Framework standard
- Squiz: Squiz Labs standard
ESLint vs PHPCS Rules
Section titled “ESLint vs PHPCS Rules”| ESLint Rule | PHPCS Rule | Description |
|---|---|---|
indent | Generic.WhiteSpace.ScopeIndent | Indentation |
semi | Squiz.PHP.DiscouragedFunctions | Semicolons |
quotes | Squiz.Strings.DoubleQuoteUsage | Quote style |
no-unused-vars | Generic.CodeAnalysis.UnusedFunctionParameter | Unused variables |
max-len | Generic.Files.LineLength | Line length |
camelcase | Squiz.NamingConventions.ValidVariableName | Naming conventions |
Custom PHPCS Rules
Section titled “Custom PHPCS Rules”phpcs.xml with custom rules:
<?xml version="1.0"?><ruleset name="Custom"> <rule ref="PSR12"> <!-- Exclude specific rules --> <exclude name="PSR2.Methods.FunctionCallSignature.SpaceAfterOpenBracket"/> </rule>
<!-- Arrays must use short syntax --> <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<!-- No trailing whitespace --> <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"> <properties> <property name="ignoreBlankLines" value="true"/> </properties> </rule>
<!-- Class names must be StudlyCaps --> <rule ref="Squiz.Classes.ValidClassName"/>
<!-- Methods must be camelCase --> <rule ref="PSR1.Methods.CamelCapsMethodName"/></ruleset>Code Formatting: Prettier vs PHP-CS-Fixer
Section titled “Code Formatting: Prettier vs PHP-CS-Fixer”Prettier Setup
Section titled “Prettier Setup”npm install --save-dev prettier.prettierrc:
{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 100, "arrowParens": "avoid"}Run:
npx prettier --write src/PHP-CS-Fixer Setup
Section titled “PHP-CS-Fixer Setup”composer require --dev friendsofphp/php-cs-fixer.php-cs-fixer.php:
<?php$finder = PhpCsFixer\Finder::create() ->in(__DIR__ . '/src') ->name('*.php');
return (new PhpCsFixer\Config()) ->setRules([ '@PSR12' => true, 'array_syntax' => ['syntax' => 'short'], 'binary_operator_spaces' => [ 'default' => 'single_space', ], 'blank_line_after_opening_tag' => true, 'concat_space' => ['spacing' => 'one'], 'function_typehint_space' => true, 'no_unused_imports' => true, 'ordered_imports' => ['sort_algorithm' => 'alpha'], 'single_quote' => true, 'trailing_comma_in_multiline' => [ 'elements' => ['arrays', 'arguments', 'parameters'], ], ]) ->setFinder($finder);Run:
vendor/bin/php-cs-fixer fixvendor/bin/php-cs-fixer fix --dry-run # Preview changesLaravel Pint (Opinionated Alternative)
Section titled “Laravel Pint (Opinionated Alternative)”Laravel Pint is a zero-config PHP code formatter built on PHP-CS-Fixer:
composer require --dev laravel/pintRun:
vendor/bin/pintvendor/bin/pint --test # Check without modifyingpint.json (optional):
{ "preset": "laravel", "rules": { "array_syntax": { "syntax": "short" }, "binary_operator_spaces": { "default": "single_space" } }}Static Analysis: TypeScript vs PHPStan/Psalm
Section titled “Static Analysis: TypeScript vs PHPStan/Psalm”TypeScript Compiler
Section titled “TypeScript Compiler”npm install --save-dev typescripttsconfig.json:
{ "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "noUnusedLocals": true, "noUnusedParameters": true }}Run:
npx tsc --noEmit # Type check without compilationPHPStan Setup
Section titled “PHPStan Setup”PHPStan performs static analysis similar to TypeScript’s type checking:
composer require --dev phpstan/phpstanphpstan.neon:
parameters: level: 8 # 0 (loose) to 9 (max strict) paths: - src excludePaths: - src/legacy/* ignoreErrors: - '#Unsafe usage of new static#' checkMissingIterableValueType: true checkGenericClassInNonGenericObjectType: trueRun:
vendor/bin/phpstan analysevendor/bin/phpstan analyse --level=5PHPStan Levels:
- Level 0: Basic checks
- Level 4: Check for unknown methods, properties
- Level 6: Check for missing type hints
- Level 8: Check for strict types
- Level 9: Maximum strictness (mixed disallowed)
Psalm Setup
Section titled “Psalm Setup”Psalm is another powerful static analysis tool (alternative to PHPStan):
composer require --dev vimeo/psalmpsalm.xml:
<?xml version="1.0"?><psalm errorLevel="4" resolveFromConfigFile="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"> <projectFiles> <directory name="src" /> <ignoreFiles> <directory name="vendor" /> </ignoreFiles> </projectFiles></psalm>Run:
vendor/bin/psalmvendor/bin/psalm --show-info=trueGeneric Type Annotations
Section titled “Generic Type Annotations”Both PHPStan and Psalm support generic type annotations via docblocks:
<?php/** * @template T * @param array<T> $items * @return T|null */function first(array $items): mixed { return $items[0] ?? null;}
/** * @template T of object */class Repository { /** * @param class-string<T> $className * @return T */ public function find(string $className, int $id): object { // ... }}
/** @var Repository<User> */$userRepo = new Repository();$user = $userRepo->find(User::class, 1); // PHPStan knows this is UserConfiguration Examples
Section titled “Configuration Examples”Complete Project Setup
Section titled “Complete Project Setup”composer.json:
{ "require-dev": { "squizlabs/php_codesniffer": "^3.7", "friendsofphp/php-cs-fixer": "^3.35", "phpstan/phpstan": "^1.10", "vimeo/psalm": "^5.15" }, "scripts": { "lint": "phpcs", "lint:fix": "phpcbf", "format": "php-cs-fixer fix", "analyse": "phpstan analyse", "psalm": "psalm", "check": [ "@lint", "@analyse", "@test" ] }}Run quality checks:
composer lint # Check code stylecomposer lint:fix # Fix code stylecomposer format # Format codecomposer analyse # Static analysiscomposer check # Run all checksEditor Integration
Section titled “Editor Integration”VS Code
Section titled “VS Code”TypeScript:
{ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode"}PHP:
{ "php.validate.enable": true, "php.validate.run": "onSave", "[php]": { "editor.defaultFormatter": "junstyle.php-cs-fixer", "editor.formatOnSave": true }, "phpstan.enabled": true, "phpstan.level": "8"}Extensions:
- PHP Intelephense
- PHP CS Fixer
- PHPStan
- Psalm
PHPStorm
Section titled “PHPStorm”PHPStorm has built-in support for all PHP quality tools:
- Settings → PHP → Quality Tools
- Configure paths to PHPCS, PHP-CS-Fixer, PHPStan
- Enable inspections
- Configure code style (PSR-12)
- Enable “Reformat Code” on save
Pre-commit Hooks
Section titled “Pre-commit Hooks”Husky (TypeScript)
Section titled “Husky (TypeScript)”npm install --save-dev husky lint-stagednpx husky installpackage.json:
{ "lint-staged": { "*.ts": [ "eslint --fix", "prettier --write" ] }}.husky/pre-commit:
#!/bin/shnpx lint-stagedGrumPHP (PHP)
Section titled “GrumPHP (PHP)”composer require --dev phpro/grumphpgrumphp.yml:
grumphp: tasks: phpcs: standard: PSR12 phpstan: level: 8 phpunit: always_execute: falseGrumPHP automatically installs git hooks.
Alternative: PHP-based Husky
Section titled “Alternative: PHP-based Husky”composer require --dev brainmaestro/composer-git-hookscomposer.json:
{ "extra": { "hooks": { "pre-commit": [ "vendor/bin/phpcs", "vendor/bin/phpstan analyse" ], "pre-push": [ "vendor/bin/phpunit" ] } }, "scripts": { "post-install-cmd": "vendor/bin/cghooks add --ignore-lock", "post-update-cmd": "vendor/bin/cghooks update" }}CI/CD Integration
Section titled “CI/CD Integration”GitHub Actions (TypeScript)
Section titled “GitHub Actions (TypeScript)”.github/workflows/lint.yml:
name: Lint
on: [push, pull_request]
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '18' - run: npm ci - run: npm run lint - run: npx tsc --noEmit - run: npm testGitHub Actions (PHP)
Section titled “GitHub Actions (PHP)”.github/workflows/quality.yml:
name: Code Quality
on: [push, pull_request]
jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.2' coverage: xdebug
- name: Install dependencies run: composer install --prefer-dist --no-progress
- name: Code Style (PHPCS) run: vendor/bin/phpcs
- name: Static Analysis (PHPStan) run: vendor/bin/phpstan analyse
- name: Run Tests run: vendor/bin/phpunit --coverage-clover coverage.xmlPractical Example: Full Quality Setup
Section titled “Practical Example: Full Quality Setup”Project Structure
Section titled “Project Structure”my-project/├── src/├── tests/├── .php-cs-fixer.php├── phpcs.xml├── phpstan.neon├── phpunit.xml├── grumphp.yml└── composer.jsoncomposer.json
Section titled “composer.json”{ "require": { "php": "^8.1" }, "require-dev": { "phpunit/phpunit": "^10.0", "squizlabs/php_codesniffer": "^3.7", "friendsofphp/php-cs-fixer": "^3.35", "phpstan/phpstan": "^1.10", "phpro/grumphp": "^2.0" }, "scripts": { "test": "phpunit", "lint": "phpcs", "lint:fix": "phpcbf", "format": "php-cs-fixer fix", "analyse": "phpstan analyse --level=8", "quality": [ "@lint", "@analyse", "@test" ] }, "autoload": { "psr-4": { "App\\": "src/" } }, "autoload-dev": { "psr-4": { "Tests\\": "tests/" } }}Run Quality Checks
Section titled “Run Quality Checks”# Check code stylecomposer lint
# Fix code style automaticallycomposer lint:fix
# Format codecomposer format
# Run static analysiscomposer analyse
# Run all quality checkscomposer qualityBest Practices
Section titled “Best Practices”1. Start with Presets
Section titled “1. Start with Presets”ESLint:
{ "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"]}PHPCS:
<rule ref="PSR12"/>PHPStan:
parameters: level: 5 # Start with level 5, increase gradually2. Enforce in CI
Section titled “2. Enforce in CI”Don’t just check locally—enforce in CI/CD:
# Fail build if quality checks don't pass- run: composer lint- run: composer analyse3. Gradual Adoption
Section titled “3. Gradual Adoption”If adding to existing project:
PHPStan with baseline:
vendor/bin/phpstan analyse --generate-baselineThis creates phpstan-baseline.neon with existing errors, allowing you to fix them incrementally.
4. Team Consistency
Section titled “4. Team Consistency”Commit configuration files:
- ✅
.php-cs-fixer.php - ✅
phpcs.xml - ✅
phpstan.neon - ✅
grumphp.yml
Key Takeaways
Section titled “Key Takeaways”- PHP_CodeSniffer (PHPCS) = ESLint for code style checking
- PHP-CS-Fixer / Pint = Prettier for automatic formatting
- PHPStan / Psalm = TypeScript compiler for static analysis at levels 0-9
- PSR-12 is the standard coding style (like Airbnb/Standard style guide)
- GrumPHP provides git hooks (like Husky) to run checks pre-commit
- Editor integration improves development experience with real-time feedback
- CI/CD integration enforces quality standards automatically on PRs
- PHPStan level 9 is strictest - catches almost all type errors
phpcbfauto-fixes code style issues found by PHPCS- Combine all three tools for comprehensive quality: PHPCS (style), PHPStan (types), tests (behavior)
.phpcs.xmlandphpstan.neonconfigure rules like.eslintrcandtsconfig.json- Rector can automatically upgrade PHP code and refactor (like jscodeshift)
Comparison Table
Section titled “Comparison Table”| Feature | TypeScript/JS | PHP |
|---|---|---|
| Style Linting | ESLint | PHP_CodeSniffer |
| Formatting | Prettier | PHP-CS-Fixer / Pint |
| Static Analysis | TypeScript compiler | PHPStan / Psalm |
| Git Hooks | Husky | GrumPHP |
| CI Integration | GitHub Actions | GitHub Actions |
| Editor Support | VS Code + extensions | VS Code / PHPStorm |
| Standards | Airbnb, Standard | PSR-12 |
Next Steps
Section titled “Next Steps”Now that you understand code quality tools, let’s explore build tools and compilation.
Next Chapter: 09: Build Tools: TypeScript Compiler vs PHP
Resources
Section titled “Resources”Questions or feedback? Open an issue on GitHub