PHP static analyzer available as a Symfony Bundle and Docker image, designed to replace
phpmetrics/phpmetrics(now unmaintained). Analyzes your PHP code and generates detailed reports on complexity, maintainability, coupling, architecture, and test coverage.
Version: 2.0.0 Author: Pascal CESCON GitHub: amoifr/PhpQuality
Version 2.0 sharply reduces false positives in architectural and SOLID analysis on framework projects. If you are upgrading from 1.x:
- The Laravel preset (
--type=laravel) is now a real preset:*ActionisApplication(not Controller),ServiceProviderisWiring(exempt from layer-violation reporting), and Eloquent / Carbon / facades / queue traits are excluded from the DIP ratio. - DIP only counts type-hinted, injectable dependencies. Old DIP scores are not directly comparable.
- A
phpquality.jsonfile at the project root can override layer rules, add to whitelists, and pin a baseline. --generate-baseline=phpquality.baseline.jsonthen--baseline=…lets you adopt the tool on existing projects without rewriting them.
See CHANGELOG.md for the full breaking changes list and
detailed migration notes.
composer require amoifr/phpquality-bundleThen register the bundle in config/bundles.php:
return [
// ...
PhpQuality\PhpQualityBundle::class => ['all' => true],
];Run the analysis:
php bin/console phpquality:analyze --source=src/# Pull the pre-built image
docker pull amoifr13/phpquality
# Run analysis
docker run --rm -v $(pwd):/project amoifr13/phpquality --source=/project/src| Metric | Description |
|---|---|
| LOC | Lines of Code (total, CLOC without comments, LLOC logical) |
| CCN | McCabe Cyclomatic Complexity per method |
| MI | Maintainability Index (0-100, higher = better) |
| LCOM | Lack of Cohesion of Methods (0-1, lower = better) |
| Halstead | Volume, Difficulty, Effort, Estimated Bugs |
| Feature | Description |
|---|---|
| Layer Detection | Auto-detection of layers (Controller, Application, Domain, Infrastructure) |
| Layer Violations | Detection of forbidden dependencies (e.g., Domain → Infrastructure) |
| SOLID Violations | Detection of SRP, OCP, ISP, DIP violations |
| Circular Dependencies | Detection of dependency cycles using Tarjan's algorithm |
| Dependency Graph | Interactive D3.js visualization |
| Dependency Matrix | Matrix view of dependencies between layers |
| Architecture Score | Global score 0-100 based on violations |
| Feature | Description |
|---|---|
| Line Coverage | Percentage of code lines covered by tests |
| Method Coverage | Percentage of methods tested |
| Class Coverage | Percentage of classes tested |
| Package Coverage | Coverage analysis by namespace/package |
| File Coverage | Coverage details for each file |
| Coverage Grade | A-F grade based on coverage percentage |
| Feature | Description |
|---|---|
| Git Blame Analysis | Per-author statistics via git blame |
| Composite Score | Score combining MI and CCN per contributor |
| Hall of Fame | Top contributors in code quality |
| Hall of Shame | Contributors whose code needs attention |
Multi-page HTML report includes:
| Page | Content |
|---|---|
| Dashboard | Overview with distribution charts |
| Documentation | Detailed explanation of each metric |
| CCN | Complexity details per method |
| MI | Maintainability index per class |
| LCOM | Cohesion per class |
| LOC | Lines of code per file |
| Halstead | Advanced complexity metrics |
| Analysis | Multi-dimensional analysis, code tree, contributors |
| Architecture | Dependency graph, SOLID violations, dependency matrix |
| Coverage | Test coverage per file and package |
| Dependencies | Composer dependency analysis |
PhpQuality adapts its analysis based on project type. Use --type= to specify the framework:
| Type | Label | Description |
|---|---|---|
php |
PHP (Generic) | Generic PHP analysis (default) |
symfony |
Symfony | Symfony Framework (Controllers, Services, Repositories...) |
laravel |
Laravel | Laravel Framework (Eloquent Models, Middleware, Jobs...) |
prestashop |
PrestaShop | PrestaShop E-commerce (Modules, ObjectModel, Hooks...) |
magento |
Magento 2 | Magento 2 E-commerce (Plugins, Observers, Blocks...) |
wordpress |
WordPress | WordPress CMS (Plugins, Themes, Hooks...) |
woocommerce |
WooCommerce | WooCommerce Extension (Gateways, Shipping...) |
drupal |
Drupal | Drupal CMS (Modules, Plugins, Forms...) |
joomla |
Joomla | Joomla CMS (Components, Modules, Plugins...) |
typo3 |
TYPO3 | TYPO3 CMS (Extensions, ViewHelpers...) |
sulu |
Sulu CMS | Sulu CMS (Symfony-based, Content Types...) |
sylius |
Sylius | Sylius E-commerce (Processors, Calculators...) |
codeigniter |
CodeIgniter | CodeIgniter Framework (Controllers, Models...) |
cakephp |
CakePHP | CakePHP Framework (Tables, Behaviors, Cells...) |
Each type defines:
- Directories to automatically exclude
- Class categorization patterns
- Recommended quality thresholds
# Analysis with auto-detection of project type
docker run --rm \
-v $(pwd):/project \
-v $(pwd)/reports:/reports \
amoifr13/phpquality \
--source=/project/src --report-html=/reports# Symfony project
docker run --rm \
-v $(pwd):/project \
-v $(pwd)/reports:/reports \
amoifr13/phpquality \
--source=/project/src --type=symfony --report-html=/reports
# PrestaShop project
docker run --rm \
-v $(pwd):/project \
amoifr13/phpquality \
--source=/project/modules/mymodule --type=prestashop
# WordPress project
docker run --rm \
-v $(pwd):/project \
amoifr13/phpquality \
--source=/project/wp-content/plugins/myplugin --type=wordpress# First generate coverage report with PHPUnit
./vendor/bin/phpunit --coverage-clover coverage.xml
# Then analyze with coverage
docker run --rm \
-v $(pwd):/project \
-v $(pwd)/reports:/reports \
amoifr13/phpquality \
--source=/project/src --coverage=/project/coverage.xml --report-html=/reportsdocker run --rm \
-v $(pwd):/project \
-v $(pwd)/reports:/reports \
amoifr13/phpquality \
--source=/project/src --git-blame --report-html=/reportsdocker run --rm \
-v $(pwd):/project \
amoifr13/phpquality \
--source=/project/src --no-htmldocker run --rm \
-v $(pwd):/project \
-v $(pwd)/reports:/reports \
amoifr13/phpquality \
--source=/project/src --json=/reports/metrics.jsondocker run --rm amoifr13/phpquality --list-typesdocker run --rm \
-v $(pwd):/project \
amoifr13/phpquality \
--source=/project/src --type=symfony --fail-on-violation| Option | Description |
|---|---|
--source, -s |
Source directory to analyze (required) |
--type, -t |
Project type (auto, symfony, laravel, etc.) |
--report-html |
Output directory for HTML report |
--json |
Output file for JSON report |
--exclude, -x |
Additional directories to exclude (repeatable) |
--no-html |
Skip HTML report generation |
--fail-on-violation |
Exit with error if violations are detected |
--git-blame |
Enable git blame analysis for Hall of Fame/Shame (slower) |
--coverage, -c |
Path to Clover XML coverage file |
--project-name, -p |
Project name to display in report titles |
--lang, -l |
Report language (en, fr, de, es, it, pt, nl, pl, ru, ja, zh, ko...) |
--list-types |
List all available project types |
--list-langs |
List all available languages |
--wizard, -w |
Interactive wizard for guided configuration |
--config |
Path to a project configuration file (default: ./phpquality.json) |
--baseline |
Path to a baseline file. Violations listed in the baseline are filtered out. |
--generate-baseline |
Write a baseline file with all current violations and exit successfully. |
Drop a phpquality.json at the root of the project being analyzed to extend
the framework preset:
{
"layers": {
"rules": [
{ "match": "App\\Custom\\**", "layer": "Application" }
]
},
"wiring": { "patterns": ["**ServiceProvider"] },
"abstractionRatio": { "ignore": ["App\\Models\\**"] },
"ignore": { "violations": ["solid.dip:App\\Foo\\Bar"] },
"baseline": "phpquality.baseline.json"
}Project rules under layers.rules are prepended to the framework preset
(first match wins). The other lists are unioned with the preset.
/**
* @phpquality-ignore solid.dip — this service intentionally wires concretes.
*/
final class FooService { /* … */ }Codes: solid.srp, solid.dip, solid.isp, architecture.layer.
# 1. Pin existing violations as accepted (do NOT fail the build)
phpquality:analyze --source=app --type=laravel \
--generate-baseline=phpquality.baseline.json
# 2. Subsequent runs only report NEW violations
phpquality:analyze --source=app --type=laravel \
--baseline=phpquality.baseline.json --fail-on-violationphpquality/
├── src/ # Symfony Bundle
│ ├── PhpQualityBundle.php
│ ├── DependencyInjection/
│ ├── Analyzer/
│ │ ├── Ast/
│ │ │ ├── AstParser.php
│ │ │ └── Visitor/
│ │ ├── Architecture/
│ │ ├── ProjectType/
│ │ ├── Result/
│ │ ├── FileAnalyzer.php
│ │ └── ProjectAnalyzer.php
│ ├── Command/
│ │ └── AnalyzeCommand.php
│ ├── Report/
│ │ ├── HtmlReportGenerator.php
│ │ └── ConsoleReportGenerator.php
│ └── Resources/
│ ├── config/services.yaml
│ ├── views/report/
│ └── translations/
├── tests/
├── docker/
│ ├── app/ # Minimal Symfony skeleton
│ ├── Dockerfile
│ └── entrypoint.sh
└── composer.json
| Component | Technology |
|---|---|
| Language | PHP 8.3 |
| Framework | Symfony 7.x |
| AST Parser | nikic/php-parser |
| HTML Rendering | Twig + Chart.js + D3.js |
| CLI | Symfony Console |
| Base Image | php:8.3-cli-alpine |
| Dependency Management | Composer |
| Score | Rating | Interpretation |
|---|---|---|
| 85-100 | A | Highly maintainable |
| 65-84 | B | Moderately maintainable |
| 40-64 | C | Difficult to maintain |
| 20-39 | D | Very difficult to maintain |
| 0-19 | F | Unmaintainable |
| Score | Rating | Interpretation |
|---|---|---|
| 1-4 | A | Low complexity |
| 5-7 | B | Moderate complexity |
| 8-10 | C | High complexity |
| 11-15 | D | Very high complexity |
| 16+ | F | Excessive complexity |
| Score | Rating | Interpretation |
|---|---|---|
| 0-0.2 | A | Excellent cohesion |
| 0.2-0.4 | B | Good cohesion |
| 0.4-0.6 | C | Moderate cohesion |
| 0.6-0.8 | D | Low cohesion |
| 0.8-1.0 | F | Very low cohesion |
| Score | Rating | Interpretation |
|---|---|---|
| 85-100 | A | Exemplary architecture |
| 70-84 | B | Good architecture |
| 50-69 | C | Acceptable architecture |
| 30-49 | D | Architecture needs improvement |
| 0-29 | F | Critical architecture |
| Score | Rating | Interpretation |
|---|---|---|
| 80-100% | A | Excellent coverage |
| 60-79% | B | Good coverage |
| 40-59% | C | Moderate coverage |
| 20-39% | D | Low coverage |
| 0-19% | F | Critical coverage |
PhpQuality automatically detects layers and checks dependency rules:
| Layer | Can depend on | Cannot depend on |
|---|---|---|
| Domain | nothing | Application, Infrastructure, Controller |
| Application | Domain | Infrastructure, Controller |
| Infrastructure | Domain, Application | Controller |
| Controller | Domain, Application, Infrastructure | - |
| Principle | Detection | Thresholds |
|---|---|---|
| SRP (Single Responsibility) | Classes with too many methods, dependencies, and lines of code | LCOM > 0.7, methods > 20, deps > 15 |
| OCP (Open/Closed) | Numerous switch/match on type | Coming soon |
| ISP (Interface Segregation) | Interfaces with too many methods | > 5 methods |
| DIP (Dependency Inversion) | Concrete/abstract dependency ratio | ratio < 0.5 |
name: Code Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: xdebug
- name: Install dependencies
run: composer install
- name: Run tests with coverage
run: ./vendor/bin/phpunit --coverage-clover coverage.xml
- name: Run PhpQuality
run: |
docker run --rm \
-v ${{ github.workspace }}:/project \
amoifr13/phpquality \
--source=/project/src \
--coverage=/project/coverage.xml \
--fail-on-violation \
--json=/project/phpquality.json
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: phpquality-report
path: phpquality.jsoncode-quality:
image: amoifr13/phpquality
script:
- phpquality:analyze --source=/builds/$CI_PROJECT_PATH/src --coverage=/builds/$CI_PROJECT_PATH/coverage.xml --fail-on-violation
artifacts:
reports:
codequality: phpquality.json- v0.1 - Basic analysis: LOC, CCN, MI, LCOM + HTML report + project types
- v0.2 - Multi-page HTML report with metrics documentation
- v0.3 - Multi-dimensional analysis, code tree, Hall of Fame/Shame, Composer dependency analysis
- v0.4 - Architecture analysis (layers, SOLID violations, D3.js dependency graph, circular dependencies)
- v1.0 - Git blame option, custom project name
- v1.1 - Test coverage analysis (Clover XML)
- v1.2 - Symfony Bundle architecture
- v1.3 - Configurable CI rules, custom thresholds
- v1.4 - PDF export, version comparison
- v2.0 - Interactive web interface, analysis history
Contributions are welcome! To propose a missing metric or fix a calculation:
- Fork the repository
- Create a branch:
git checkout -b feature/my-metric - Commit your changes
- Open a Pull Request
MIT - see LICENSE file.
- phpmetrics/PhpMetrics (GitHub) - original project
- nikic/php-parser
- Halstead complexity measures (Wikipedia)
- Cyclomatic complexity (Wikipedia)
- Software package metrics - Robert Martin (Wikipedia)
- Deptrac (GitHub) - inspiration for layer analysis
- PHP Insights (GitHub) - inspiration for quality analysis
- SOLID principles (Wikipedia)
- Clean Architecture - Robert C. Martin
- PHPUnit Code Coverage