# Quality Tools for TYPO3 Development Automated code quality and static analysis tools for TYPO3 extensions. ## Overview - **PHPStan**: Static analysis for type safety and bugs - **Rector**: Automated code refactoring and modernization - **php-cs-fixer**: Code style enforcement (PSR-12, TYPO3 CGL) - **phplint**: PHP syntax validation ## PHPStan ### Installation ```bash composer require --dev phpstan/phpstan phpstan/phpstan-strict-rules saschaegerer/phpstan-typo3 ``` ### Configuration Create `Build/phpstan.neon`: ```neon includes: - vendor/phpstan/phpstan-strict-rules/rules.neon - vendor/saschaegerer/phpstan-typo3/extension.neon parameters: level: max # Level 10 - maximum strictness paths: - Classes - Tests excludePaths: - Tests/Acceptance/_output/* reportUnmatchedIgnoredErrors: true checkGenericClassInNonGenericObjectType: false checkMissingIterableValueType: false ``` ### Running PHPStan ```bash # Via runTests.sh Build/Scripts/runTests.sh -s phpstan # Directly vendor/bin/phpstan analyze --configuration Build/phpstan.neon # With baseline (ignore existing errors) vendor/bin/phpstan analyze --generate-baseline Build/phpstan-baseline.neon # Clear cache vendor/bin/phpstan clear-result-cache ``` ### PHPStan Rule Levels **Level 0-10** (use `max` for level 10): Increasing strictness - **Level 0**: Basic checks (undefined variables, unknown functions) - **Level 5**: Type checks, unknown properties, unknown methods - **Level 9**: Strict mixed types, unused parameters - **Level 10 (max)**: Maximum strictness - explicit mixed types, pure functions **Recommendation**: Start with level 5, aim for level 10 (max) in modern TYPO3 13 projects. **Why Level 10?** - Enforces explicit type declarations (`mixed` must be declared, not implicit) - Catches more potential bugs at development time - Aligns with TYPO3 13 strict typing standards (`declare(strict_types=1)`) - Required for PHPStan Level 10 compliant extensions ### Ignoring Errors ```php /** @phpstan-ignore-next-line */ $value = $this->legacyMethod(); // Or in neon file parameters: ignoreErrors: - '#Call to an undefined method.*::getRepository\(\)#' ``` ### TYPO3-Specific Rules ```php // PHPStan understands TYPO3 classes $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable('pages'); // ✅ PHPStan knows this returns QueryBuilder // Detects TYPO3 API misuse TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(MyService::class); // ✅ Checks if MyService is a valid class ``` ## Rector ### Installation ```bash composer require --dev rector/rector ssch/typo3-rector ``` ### Configuration Create `rector.php`: ```php withPaths([ __DIR__ . '/Classes', __DIR__ . '/Tests', ]) ->withSkip([ __DIR__ . '/Tests/Acceptance/_output', ]) ->withPhpSets(php82: true) ->withSets([ LevelSetList::UP_TO_PHP_82, SetList::CODE_QUALITY, SetList::DEAD_CODE, SetList::TYPE_DECLARATION, Typo3SetList::TYPO3_13, ]); ``` ### Running Rector ```bash # Dry run (show changes) vendor/bin/rector process --dry-run # Apply changes vendor/bin/rector process # Via runTests.sh Build/Scripts/runTests.sh -s rector ``` ### Common Refactorings **TYPO3 API Modernization**: ```php // Before $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'pages', 'uid=1'); // After (Rector auto-refactors) GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable('pages') ->select(['*'], 'pages', ['uid' => 1]) ->fetchAllAssociative(); ``` **Type Declarations**: ```php // Before public function process($data) { return $data; } // After public function process(array $data): array { return $data; } ``` ## php-cs-fixer ### Installation ```bash composer require --dev friendsofphp/php-cs-fixer ``` ### Configuration Create `Build/php-cs-fixer.php`: ```php in(__DIR__ . '/../Classes') ->in(__DIR__ . '/../Tests') ->exclude('_output'); return (new PhpCsFixer\Config()) ->setRules([ '@PSR12' => true, '@PhpCsFixer' => true, 'array_syntax' => ['syntax' => 'short'], 'concat_space' => ['spacing' => 'one'], 'declare_strict_types' => true, 'ordered_imports' => ['sort_algorithm' => 'alpha'], 'no_unused_imports' => true, 'single_line_throw' => false, 'phpdoc_align' => false, 'phpdoc_no_empty_return' => false, 'phpdoc_summary' => false, ]) ->setRiskyAllowed(true) ->setFinder($finder); ``` ### Running php-cs-fixer ```bash # Check only (dry run) vendor/bin/php-cs-fixer fix --config Build/php-cs-fixer.php --dry-run --diff # Fix files vendor/bin/php-cs-fixer fix --config Build/php-cs-fixer.php # Via runTests.sh Build/Scripts/runTests.sh -s cgl ``` ### Common Rules ```php // array_syntax: short $array = [1, 2, 3]; // ✅ $array = array(1, 2, 3); // ❌ // concat_space: one $message = 'Hello ' . $name; // ✅ $message = 'Hello '.$name; // ❌ // declare_strict_types **Security Note**: `composer audit` checks for known security vulnerabilities in dependencies. Run this regularly and especially before releases. ## Pre-commit Hook Create `.git/hooks/pre-commit`: ```bash #!/bin/sh echo "Running quality checks..." # Lint vendor/bin/phplint || exit 1 # PHPStan vendor/bin/phpstan analyze --configuration Build/phpstan.neon --error-format=table --no-progress || exit 1 # Code style vendor/bin/php-cs-fixer fix --config Build/php-cs-fixer.php --dry-run --diff || exit 1 echo "✓ All checks passed" ``` ## CI/CD Integration ### GitHub Actions ```yaml quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: php-version: '8.4' # Use latest PHP for quality tools - run: composer install - run: composer ci:test:php:lint - run: composer ci:test:php:phpstan - run: composer ci:test:php:cgl - run: composer ci:test:php:rector - run: composer ci:test:php:security ``` ## IDE Integration ### PHPStorm 1. **PHPStan**: Settings → PHP → Quality Tools → PHPStan 2. **php-cs-fixer**: Settings → PHP → Quality Tools → PHP CS Fixer 3. **File Watchers**: Auto-run on file save ### VS Code ```json { "php.validate.executablePath": "/usr/bin/php", "phpstan.enabled": true, "phpstan.configFile": "Build/phpstan.neon", "php-cs-fixer.onsave": true, "php-cs-fixer.config": "Build/php-cs-fixer.php" } ``` ## Best Practices 1. **PHPStan Level 10**: Aim for `level: max` in modern TYPO3 13 projects 2. **Baseline for Legacy**: Use baselines to track existing issues during migration 3. **Security Audits**: Run `composer audit` regularly and in CI 4. **Auto-fix in CI**: Run fixes automatically, fail on violations 5. **Consistent Rules**: Share config across team 6. **Pre-commit Checks**: Catch issues before commit (lint, PHPStan, CGL, security) 7. **Latest PHP**: Run quality tools with latest PHP version (8.4+) 8. **Regular Updates**: Keep tools and rules updated ## Resources - [PHPStan Documentation](https://phpstan.org/user-guide/getting-started) - [Rector Documentation](https://getrector.com/documentation) - [PHP CS Fixer Documentation](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) - [TYPO3 Coding Guidelines](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/CodingGuidelines/)