PHP-QA-CI: A Comprehensive Quality Assurance Pipeline in a Single Dependency
Setting up quality assurance for PHP projects is a pain. You need to install a dozen different tools, each with its own config files and quirks. But what if you could get a complete, battle-tested QA pipeline with just one Composer dependency?
The Problem with Traditional QA Setup
The traditional approach to setting up QA tools is time-consuming:
- Installing multiple dev dependencies individually
- Creating configuration files for each tool
- Writing scripts to run tools in the correct order
- Ensuring consistency across different projects
- Maintaining and updating configurations as tools evolve
- Training team members on different tool interfaces
This fragmented approach creates problems: you get inconsistent setups across projects, maintenance becomes a nightmare, and teams often skip important QA steps because it's just too complicated.
PHP-QA-CI
PHP-QA-CI solves these problems. Built by Long Term Support LTD, it gives you a complete QA pipeline through a single Composer dependency.
The key innovation is simple: instead of manually orchestrating multiple tools, PHP-QA-CI provides:
- Pre-configured, sensible defaults for all integrated tools
- Logical execution order that fails fast on errors
- Consistent interface across all projects
- Easy customisation when needed
- Version-specific branches for different PHP versions
The Complete Tool Suite
Installing PHP-QA-CI gives you immediate access to a complete suite of QA tools, organised into logical categories:
The tools run in order from fastest to slowest, giving you quick feedback:
1. Validation and Checks
- PSR-4 Validation - Checks code namespaces for PSR-4 compliance (built-in script)
- Composer Check for Issues - Runs composer diagnose and dumps the autoloader
- Strict Types Enforcing - Finds and fixes files missing strict types declarations
2. Linting
- PHP Parallel Lint - Lightning-fast PHP syntax error checking
3. Static Analysis
- PHPStan - Static code analysis
4. Testing
- PHPUnit - Unit testing
- Infection - Mutation testing that deliberately breaks your code to test how good your tests are
5. Documentation
- Markdown Links Checker - Finds broken links in README.md and docs files (built-in script)
6. Final Checks
- Uncommitted Changes Check - Makes sure you don't have uncommitted changes
7. Code Formatting
- Beautifier and Fixer - Automatically formats PHP code and applies coding standards
- PHP Code Sniffer - Catches any remaining coding standards violations
Installation and Basic Usage
Getting started with PHP-QA-CI requires just a single Composer command:
{
"require-dev": {
"lts/php-qa-ci": "dev-php8.4@dev"
},
"config": {
"bin-dir": "bin"
}
}
Install the package (using the PHP 8.4 branch):
composer require --dev lts/php-qa-ci:dev-php8.4@dev
That gives you access to the complete QA pipeline:
#!/bin/bash
# Run the complete QA pipeline
./bin/qa
# Run with a specific PHP version
export PHP_QA_CI_PHP_EXECUTABLE=/usr/bin/php8.3
./bin/qa
# Run on a specific directory
./bin/qa /path/to/scan
# Run a single tool (example with PHPStan)
./bin/qa -t stan
The pipeline runs tools in an order designed to "fail fast," catching basic issues first before running the more time-consuming analyses.
PHP Version Support
PHP-QA-CI maintains separate branches for different PHP versions. This ensures compatibility and lets you use version-specific features:
php8.3- PHP 8.3 specific configurationsphp8.4- PHP 8.4 support (current recommended branch as of 2025)php8.5- Default branch, tracking the latest PHP release
This branching strategy gives you sensible configurations for each PHP version, whilst still maintaining backward compatibility when needed.
Configuration and Customisation
PHP-QA-CI works out of the box with sensible defaults, but it's easy to customise. The tool looks for custom configurations in your project's qaConfig directory. If it doesn't find them, it uses the defaults.
Creating Custom Configurations
#!/bin/bash
# Create custom configuration directory
mkdir -p qaConfig
# Copy and customise PHPStan configuration
cp vendor/lts/php-qa-ci/configDefaults/generic/phpstan.neon qaConfig/
# Edit qaConfig/phpstan.neon as needed
# Copy and customise PHP CS Fixer configuration
cp vendor/lts/php-qa-ci/configDefaults/generic/php_cs.php qaConfig/
# Edit qaConfig/php_cs.php as needed
# The QA pipeline will automatically use your custom configs
PHPStan Custom Configuration Example
Here's how to extend the default PHPStan configuration for your project:
includes:
- vendor/lts/php-qa-ci/configDefaults/generic/phpstan.neon
parameters:
level: 9
paths:
- src
- tests
excludePaths:
- tests/fixtures
ignoreErrors:
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\)#'
reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false
PHP CS Fixer Custom Configuration
Customise coding standards whilst maintaining the base configuration:
<?php
declare(strict_types=1);
// Include the default configuration
$config = require __DIR__ . '/../vendor/lts/php-qa-ci/configDefaults/generic/php_cs.php';
// Get the default finder
$finder = require __DIR__ . '/../vendor/lts/php-qa-ci/configDefaults/generic/php_cs_finder.php';
// Customise the finder if needed
$finder
->exclude('legacy')
->notPath('src/Migrations/Version*.php');
// Add or override rules
$rules = $config->getRules();
$rules['php_unit_test_class_requires_covers'] = false;
$rules['php_unit_internal_class'] = false;
return $config
->setFinder($finder)
->setRules($rules);
Symfony Project Integration
PHP-QA-CI includes special considerations for Symfony projects. When installing in a Symfony project, you have two options:
#!/bin/bash
# For Symfony projects: choose between Symfony defaults or php-qa-ci defaults
# Option 1: Use php-qa-ci defaults (more extensive)
rm phpunit.xml.dist
ln -s vendor/lts/php-qa-ci/configDefaults/generic/phpunit.xml
# Option 2: Keep Symfony defaults
# Accept the recipe prompts during installation
# Create project-specific overrides
mkdir -p qaConfig
echo "parameters:
level: 8
paths:
- src
excludePaths:
- var
- vendor" > qaConfig/phpstan.neon
The PHP-QA-CI defaults are more comprehensive than Symfony's defaults, giving you additional static analysis rules and stricter coding standards.
Advanced Features
Hooks System
PHP-QA-CI supports pre and post execution hooks, allowing you to integrate custom logic into the pipeline:
#!/bin/bash
# Create custom hooks for the QA pipeline
mkdir -p qaConfig
# Pre-hook: Run before QA pipeline starts
cat > qaConfig/preBashHook.bash << 'EOF'
#!/bin/bash
echo "Starting QA pipeline for project: $(basename $(pwd))"
echo "PHP Version: $($PHP_QA_CI_PHP_EXECUTABLE --version | head -n1)"
# Clear any caches that might affect results
if [ -d "var/cache" ]; then
rm -rf var/cache/*
fi
EOF
# Post-hook: Run after QA pipeline completes
cat > qaConfig/postBashHook.bash << 'EOF'
#!/bin/bash
echo "QA Pipeline completed!"
# Generate a summary report
if [ -f "infection.log" ]; then
echo "Mutation Score: $(grep -oP 'Mutation Score Indicator \(MSI\): \K[0-9.]+' infection.log)%"
fi
# Send notification (example)
# curl -X POST https://hooks.slack.com/services/YOUR/WEBHOOK/URL \
# -H 'Content-type: application/json' \
# --data '{"text":"QA Pipeline completed successfully!"}'
EOF
chmod +x qaConfig/*.bash
Mutation Testing with Infection
One of the most powerful features is mutation testing via Infection, which tests the quality of your test suite by introducing small changes to your code and then checking whether your tests catch these mutations:
{
"source": {
"directories": ["src"]
},
"logs": {
"text": "infection.log",
"summary": "summary.log",
"debug": "debug.log",
"perMutator": "per-mutator.md"
},
"mutators": {
"@default": true,
"global-ignoreSourceCodeByRegex": ["Assert::.*"]
},
"testFramework": "phpunit",
"testFrameworkOptions": "--configuration=phpunit.xml"
}
Performance Optimisation
The pipeline is optimised for performance in several ways:
- Fail-fast approach - basic checks run first
- Parallel execution where possible
- Intelligent caching of results
- Option to run quick tests only during development
CI/CD Integration
PHP-QA-CI works well in CI environments. Here's an example GitHub Actions workflow:
name: Quality Assurance
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
qa:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['8.1', '8.2', '8.3']
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: xdebug
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run QA Pipeline
run: ./bin/qa
env:
PHP_QA_CI_PHP_EXECUTABLE: /usr/bin/php${{ matrix.php-version }}
The pipeline works just as well with GitLab CI, Jenkins, or Bitbucket Pipelines. The consistent interface means your local development experience matches your CI environment exactly.
Real-World Benefits
Consistency Across Projects
With PHP-QA-CI, all your projects use the same QA pipeline, making it easy for developers to move between projects without learning new tools or configurations.
Time Savings
Setting up a complete QA pipeline manually takes hours or even days. With PHP-QA-CI, you're up and running in minutes with a battle-tested configuration.
Maintenance Reduction
Instead of maintaining configurations for a dozen tools across multiple projects, you maintain just one dependency. Tool configuration updates are handled centrally.
Best Practices by Default
The default configurations include years of PHP development best practices. Your code meets high quality standards without you having to research what those standards should be.
Comparison with Manual Setup
Consider what manual setup of these tools would require:
| Aspect | Manual Setup | PHP-QA-CI |
|---|---|---|
| Execution Scripts | Custom required | Single qa command |
| Cross-project Consistency | Manual synchronisation | Automatic |
| Tool Updates | Individual updates | Single update |
Troubleshooting Common Issues
Permission Issues
If you encounter permission issues with the qa script:
chmod +x vendor/lts/php-qa-ci/bin/qa
# Or use composer's bin directory
chmod +x bin/qa
Memory Limits
Some tools like PHPStan may require increased memory limits:
export PHP_QA_CI_PHP_EXECUTABLE="php -d memory_limit=512M"
./bin/qa
Tool-Specific Issues
Individual tools can be run in isolation for debugging:
# Run only PHPStan
./bin/qa -t stan
# Run with verbose output
./bin/qa --verbose
Future Development
PHP-QA-CI keeps evolving with the PHP ecosystem. Current development focuses on:
- Support for newer PHP versions as they're released
- Integration of emerging QA tools
- Performance optimisations for large codebases
- Enhanced reporting and metrics
- Better IDE integration support
Why This Matters for PHP Teams
PHP-QA-CI changes how you set up quality assurance for PHP projects, providing a complete, pre-configured pipeline through a single dependency that removes the barriers to implementing comprehensive quality checks.
Whether you're starting a new project or improving QA in an existing codebase, PHP-QA-CI offers immediate value with minimal setup, thanks to the combination of sensible defaults, easy customisation, and comprehensive tool coverage.
I think it fits the philosophy of the PHP language itself: pragmatic and focused on developer productivity rather than ceremony. By abstracting away QA pipeline complexity, PHP-QA-CI leaves developers more time to spend on the actual code.
Get Started Today
Visit the PHP-QA-CI GitHub repository or install it directly via Composer:
composer require --dev lts/php-qa-ci:dev-php8.4@dev
Got a PHP system that needs this kind of attention?
Get in touch