ctw / ctw-qa
Configuration for commonly used quality assurance (QA) tools for PHP projects.
Installs: 1 510
Dependents: 16
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/ctw/ctw-qa
Requires
- php: ^8.3
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- rector/rector: ^2.0
- symplify/easy-coding-standard: ^13.0
Requires (Dev)
- phpunit/phpunit: ^12.0
- symfony/var-dumper: ^7.0
- dev-master
- 5.0.11
- 5.0.10
- 5.0.9
- 5.0.8
- 5.0.7
- 5.0.6
- 5.0.5
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.0.6
- 4.0.5
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 1.0.29
- 1.0.28
- 1.0.27
- 1.0.26
- 1.0.25
- 1.0.24
- 1.0.23
- 1.0.22
- 1.0.21
- 1.0.20
- 1.0.19
- 1.0.18
- 1.0.17
- 1.0.16
- 1.0.15
- 1.0.14
- 1.0.13
- 1.0.12
- 1.0.11
- 1.0.10
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
This package is auto-updated.
Last update: 2025-11-26 08:00:16 UTC
README
Centralized, opinionated configuration for PHP 8.3+ quality assurance tools: Rector, Easy Coding Standard (ECS), and PHPStan.
Introduction
Why This Library Exists
Setting up quality assurance tools properly is tedious and error-prone. Each project requires configuring:
- Rector for code modernization (100+ lines of configuration)
- Easy Coding Standard for style enforcement (150+ lines)
- PHPStan for static analysis (50+ lines)
Multiplied across numerous projects, this becomes a maintenance burden. Configurations drift, standards diverge, and teams waste time debugging tool setups instead of writing code.
This library provides:
- Battle-tested defaults: Years of PHP best practices encoded in reusable configuration classes
- PHP 8.3+ standards: Modern PHP syntax, strict types, and property promotion
- PSR-12 compliance: Full adherence to PHP coding standards
- Extensible architecture: Override any default via class inheritance
- Consistent tooling: Identical QA configuration across all projects
Problems This Library Solves
- Configuration drift: Projects diverge in coding standards over time
- Repetitive setup: Same boilerplate written for every new project
- Inconsistent quality: Different strictness levels across codebases
- Maintenance burden: Tool updates require changes in multiple places
- Onboarding friction: New developers must learn project-specific configurations
Where to Use This Library
- New PHP projects: Start with modern, strict standards from day one
- Existing codebases: Gradually modernize legacy code with Rector
- Monorepos: Share identical QA configuration across all packages
- CI/CD pipelines: Automated quality gates with consistent rules
- Open source libraries: Enforce professional-grade code quality
Design Goals
- Opinionated defaults: Strong opinions for modern PHP, easily overridable
- Invokable classes: Simple
$config()syntax for integration - Maximum strictness: PHPStan level
max, strict comparisons, strict types - Minimal dependencies: Only the three QA tools themselves
- Extensible: All configuration classes designed for inheritance
Requirements
- PHP 8.3 or higher
- Composer
Installation
Install by adding the package as a Composer requirement:
composer require ctw/ctw-qa --dev
Usage Examples
Rector Configuration
Create rector.php in your project root:
<?php declare(strict_types=1); use Ctw\Qa\Rector\Config\RectorConfig\DefaultFileExtensions; use Ctw\Qa\Rector\Config\RectorConfig\DefaultSets; use Ctw\Qa\Rector\Config\RectorConfig\DefaultSkip; use Rector\Config\RectorConfig; return static function (RectorConfig $rectorConfig): void { $fileExtensions = new DefaultFileExtensions(); $sets = new DefaultSets(); $skip = new DefaultSkip(); $rectorConfig->fileExtensions($fileExtensions()); $rectorConfig->sets($sets()); $rectorConfig->paths(['src', 'test']); $rectorConfig->skip([...$skip()]); };
ECS Configuration
Create ecs.php in your project root:
<?php declare(strict_types=1); use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultFileExtensions; use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultIndentation; use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultLineEnding; use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultRules; use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultRulesWithConfiguration; use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultSets; use Ctw\Qa\EasyCodingStandard\Config\ECSConfig\DefaultSkip; use Symplify\EasyCodingStandard\Config\ECSConfig; return static function (ECSConfig $ecsConfig): void { $fileExtensions = new DefaultFileExtensions(); $indentation = new DefaultIndentation(); $lineEnding = new DefaultLineEnding(); $rules = new DefaultRules(); $rulesConfig = new DefaultRulesWithConfiguration(); $sets = new DefaultSets(); $skip = new DefaultSkip(); $ecsConfig->fileExtensions($fileExtensions()); $ecsConfig->indentation($indentation()); $ecsConfig->lineEnding($lineEnding()); $ecsConfig->paths(['src', 'test']); $ecsConfig->sets($sets()); $ecsConfig->rules($rules()); $ecsConfig->rulesWithConfiguration($rulesConfig()); $ecsConfig->skip($skip()); };
PHPStan Configuration
Create phpstan.neon in your project root:
parameters: level: max paths: - src - test bootstrapFiles: - vendor/autoload.php
Composer Scripts
Add to your composer.json:
{
"scripts": {
"qa": ["@rector", "@ecs", "@phpstan"],
"qa-fix": ["@rector-fix", "@ecs-fix", "@phpstan"],
"rector": "vendor/bin/rector process --dry-run",
"rector-fix": "vendor/bin/rector process",
"ecs": "vendor/bin/ecs",
"ecs-fix": "vendor/bin/ecs --fix",
"phpstan": "vendor/bin/phpstan analyse"
}
}
Run QA checks:
composer qa # Check everything (dry-run) composer qa-fix # Auto-fix everything possible
Included Rule Sets
Rector (Code Modernization)
| Set | Description |
|---|---|
UP_TO_PHP_83 |
Modernizes code to PHP 8.3 syntax |
PHPUNIT_100 |
Upgrades PHPUnit to version 10.0+ |
CODE_QUALITY |
Simplifies expressions, removes redundancy |
CODING_STYLE |
Enforces consistent style |
DEAD_CODE |
Removes unused code |
NAMING |
Improves naming conventions |
ECS (Code Style)
| Rule | Description |
|---|---|
DeclareStrictTypesFixer |
Adds declare(strict_types=1) |
DisallowLongArraySyntaxSniff |
Enforces short array syntax [] |
StrictComparisonFixer |
Enforces === over == |
NoUnusedImportsFixer |
Removes unused imports |
OrderedImportsFixer |
Alphabetizes imports |
TrailingCommaInMultilineFixer |
Adds trailing commas |
PHPStan (Static Analysis)
- Level:
max(strictest) - Strict rules enabled
- PHPUnit extension included
Customization
Extend any configuration class to modify defaults:
<?php declare(strict_types=1); namespace App\QA; use Ctw\Qa\Rector\Config\RectorConfig\DefaultSkip; class CustomSkip extends DefaultSkip { public function __invoke(): array { $skip = parent::__invoke(); $skip[] = '*/legacy/*'; return $skip; } }
Use your custom class:
$skip = new \App\QA\CustomSkip(); $rectorConfig->skip([...$skip()]);