Skip to content

Commit 716df2f

Browse files
authored
Merge pull request #1 from Amoifr/dev
v2.0.0 — eliminate Laravel false positives
2 parents 7304d7e + 701479b commit 716df2f

34 files changed

Lines changed: 2411 additions & 234 deletions

CHANGELOG.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,97 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.0.0] - 2026-05-06
11+
12+
Major release focused on **dramatically reducing false positives** in
13+
architectural / SOLID analysis on framework projects (Laravel and Symfony).
14+
The headline change is that `--type=laravel` is now a real, opinionated preset
15+
instead of a near-no-op flag.
16+
17+
### Breaking changes
18+
19+
- **Layer classification is now rule-based.** The previous
20+
`(layer ⇒ namespace[] + suffix[])` lookup is replaced by an ordered list of
21+
glob `LayerRule` objects (first match wins). `array_merge_recursive` is gone.
22+
Custom `ProjectTypeInterface` implementations must implement the new
23+
`getLayerRules()` method (defaults provided in `AbstractProjectType`).
24+
- **`*Action` is no longer classified as `Controller` in Laravel.** It maps to
25+
`Application` (use case). This eliminates ~12 spurious
26+
"Infrastructure → Controller" violations on a typical Laravel app using the
27+
Action pattern.
28+
- **`Wiring` is now a layer.** `ServiceProvider`, container extensions and
29+
files under `**\\Providers\\**` are flagged as `Wiring`, exempt from layer
30+
violation reporting and from DIP scoring. Their job is precisely to bind
31+
concrete classes from any layer, so the previous violations were noise.
32+
- **DIP scope reduced to injected dependencies.** Only `type_hint_param`,
33+
`type_hint_return`, and `type_hint_property` count toward the abstraction
34+
ratio. `extends`, `trait`, `new`, `static_call`, `instanceof`, `catch`,
35+
`implements`, `const`, `use` no longer pollute the metric. Old DIP scores
36+
are not comparable.
37+
- **DIP whitelist by default.** The Laravel preset excludes Eloquent
38+
primitives, Carbon, Closure, facades, queue/bus traits, and OpenAPI
39+
attributes from the DIP ratio. The Symfony preset excludes Doctrine ORM,
40+
HttpFoundation, Form, Validator, Security, Twig, etc. This eliminates the
41+
pathological case of e.g. `OrganizationController` flagged with
42+
`104/104 concrete dependencies`.
43+
- **`ProjectTypeInterface` gains 3 methods**: `getLayerRules()`,
44+
`getDipIgnoreList()`, `getWiringPatterns()`. `AbstractProjectType` provides
45+
generic defaults so existing custom types still compile, but presets that
46+
want framework-aware behaviour must override them.
47+
- **`SolidAnalyzer::analyze()` and `ArchitectureAnalyzer::analyze()` signatures
48+
changed.** `SolidAnalyzer::analyze(array $fileResults, array $dipIgnoreList = [], array $wiringClasses = [], array $classIgnores = [])`; `ArchitectureAnalyzer::analyze(array $fileResults, ?ProjectTypeInterface $projectType = null, ?ProjectConfig $config = null)`.
49+
- **`ProjectAnalyzer` constructor now requires `ProjectConfigLoader` and
50+
`BaselineManager`.** Symfony autowiring picks them up automatically.
51+
52+
### Added
53+
54+
- **`phpquality.json` project configuration file** at the project root.
55+
Schema (all keys optional):
56+
```json
57+
{
58+
"layers": { "rules": [{ "match": "App\\Foo\\**", "layer": "Application" }] },
59+
"wiring": { "patterns": ["**ServiceProvider"] },
60+
"abstractionRatio": { "ignore": ["App\\Models\\**"] },
61+
"ignore": { "violations": ["solid.dip:App\\Foo\\Bar"] },
62+
"baseline": "phpquality.baseline.json"
63+
}
64+
```
65+
`layers.rules` is **prepended** to the framework preset (project rules win).
66+
`wiring.patterns`, `abstractionRatio.ignore`, `ignore.violations` are
67+
**unioned** with framework defaults.
68+
- **`@phpquality-ignore` docblock annotation** on classes and interfaces:
69+
```php
70+
/** @phpquality-ignore solid.dip — wiring intentionnel */
71+
final class FooService { … }
72+
```
73+
Codes: `solid.srp`, `solid.dip`, `solid.isp`, `architecture.layer`.
74+
- **Baseline generation and application**.
75+
- `--generate-baseline=phpquality.baseline.json` writes a hash-keyed list of
76+
all current violations and exits successfully (does not fail-on-violation).
77+
- `--baseline=phpquality.baseline.json` filters violations whose hash is in
78+
the baseline. The summary exposes `suppressedByBaseline` and warns about
79+
`obsoleteBaselineEntries` (entries that no longer match anything →
80+
regenerate).
81+
- New CLI options: `--config`, `--baseline`, `--generate-baseline`.
82+
- New services: `PhpQuality\Config\ProjectConfigLoader`,
83+
`PhpQuality\Config\BaselineManager`, `PhpQuality\Config\ProjectConfig`,
84+
`PhpQuality\Config\LayerRule`,
85+
`PhpQuality\Analyzer\Ast\IgnoreAnnotationParser`.
86+
87+
### Migrating from 1.x
88+
89+
Most users only need to run the new version: false positives drop on their
90+
own. If you previously had a CI policy on the violation counts, expect those
91+
counts to **decrease**.
92+
93+
If you have a custom `ProjectTypeInterface` implementation:
94+
1. Have it extend `AbstractProjectType` (gets the 3 new methods for free), OR
95+
2. Implement `getLayerRules()`, `getDipIgnoreList()`, `getWiringPatterns()`.
96+
97+
If you want to keep some violations in your reports while you migrate, pin
98+
them via `--generate-baseline` and commit the baseline file. Subsequent runs
99+
with `--baseline=…` will only show new violations.
100+
10101
## [1.6.0] - 2026-03-26
11102

12103
### Added
@@ -107,7 +198,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
107198
### Fixed
108199
- Allow running Docker container with any user (`--user` flag)
109200

110-
[Unreleased]: https://github.com/amoifr/PhpQuality/compare/v1.6.0...HEAD
201+
[Unreleased]: https://github.com/amoifr/PhpQuality/compare/v2.0.0...HEAD
202+
[2.0.0]: https://github.com/amoifr/PhpQuality/compare/v1.6.0...v2.0.0
111203
[1.6.0]: https://github.com/amoifr/PhpQuality/compare/v1.5.0...v1.6.0
112204
[1.5.0]: https://github.com/amoifr/PhpQuality/compare/v1.4.1...v1.5.0
113205
[1.4.1]: https://github.com/amoifr/PhpQuality/compare/v1.4.0...v1.4.1

DOCKERHUB.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ docker run --rm \
7777
| `--fail-on-violation` | Exit with error if violations found (CI mode) |
7878
| `--list-types` | List available project types |
7979
| `--list-langs` | List available languages |
80+
| `--config` | Path to a project configuration file (default: `./phpquality.json`) |
81+
| `--baseline` | Filter out violations listed in the baseline file |
82+
| `--generate-baseline` | Write a baseline of current violations and exit successfully |
83+
84+
---
85+
86+
## What's new in 2.0
87+
88+
- The Laravel preset (`--type=laravel`) is now a real preset: `*Action` is
89+
classified as `Application`, `ServiceProvider` as `Wiring` (exempt from
90+
layer violations), and Eloquent / Carbon / facades / queue traits are
91+
excluded from the DIP ratio.
92+
- DIP only counts type-hinted, injectable dependencies — no more false
93+
positives from `use Trait;` or PHP attributes.
94+
- New `phpquality.json` for project-level overrides, `@phpquality-ignore`
95+
docblock annotation, and a `--baseline` workflow to adopt the tool on
96+
existing projects without rewriting them.
97+
98+
See [CHANGELOG.md](https://github.com/Amoifr/phpquality/blob/main/CHANGELOG.md) for the full migration guide.
8099

81100
---
82101

README.md

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,33 @@
33
> PHP static analyzer available as a Symfony Bundle and Docker image, designed to replace `phpmetrics/phpmetrics` (now unmaintained).
44
> Analyzes your PHP code and generates detailed reports on complexity, maintainability, coupling, architecture, and test coverage.
55
6-
**Version:** 1.2.0
6+
**Version:** 2.0.0
77
**Author:** [Pascal CESCON](https://moi.ruedesjasses.fr)
88
**GitHub:** [amoifr/PhpQuality](https://github.com/amoifr/PhpQuality)
99

1010
---
1111

12+
## Migrating from 1.x
13+
14+
Version **2.0** sharply reduces false positives in architectural and SOLID
15+
analysis on framework projects. If you are upgrading from 1.x:
16+
17+
- The Laravel preset (`--type=laravel`) is now a real preset: `*Action` is
18+
`Application` (not Controller), `ServiceProvider` is `Wiring` (exempt from
19+
layer-violation reporting), and Eloquent / Carbon / facades / queue traits
20+
are excluded from the DIP ratio.
21+
- DIP only counts type-hinted, injectable dependencies. Old DIP scores are
22+
not directly comparable.
23+
- A `phpquality.json` file at the project root can override layer rules,
24+
add to whitelists, and pin a baseline.
25+
- `--generate-baseline=phpquality.baseline.json` then `--baseline=…` lets
26+
you adopt the tool on existing projects without rewriting them.
27+
28+
See [`CHANGELOG.md`](CHANGELOG.md) for the full breaking changes list and
29+
detailed migration notes.
30+
31+
---
32+
1233
## Installation
1334

1435
### As a Symfony Bundle
@@ -247,6 +268,55 @@ docker run --rm \
247268
| `--lang`, `-l` | Report language (en, fr, de, es, it, pt, nl, pl, ru, ja, zh, ko...) |
248269
| `--list-types` | List all available project types |
249270
| `--list-langs` | List all available languages |
271+
| `--wizard`, `-w` | Interactive wizard for guided configuration |
272+
| `--config` | Path to a project configuration file (default: `./phpquality.json`) |
273+
| `--baseline` | Path to a baseline file. Violations listed in the baseline are filtered out. |
274+
| `--generate-baseline` | Write a baseline file with all current violations and exit successfully. |
275+
276+
### Project configuration file (`phpquality.json`, since 2.0)
277+
278+
Drop a `phpquality.json` at the root of the project being analyzed to extend
279+
the framework preset:
280+
281+
```json
282+
{
283+
"layers": {
284+
"rules": [
285+
{ "match": "App\\Custom\\**", "layer": "Application" }
286+
]
287+
},
288+
"wiring": { "patterns": ["**ServiceProvider"] },
289+
"abstractionRatio": { "ignore": ["App\\Models\\**"] },
290+
"ignore": { "violations": ["solid.dip:App\\Foo\\Bar"] },
291+
"baseline": "phpquality.baseline.json"
292+
}
293+
```
294+
295+
Project rules under `layers.rules` are **prepended** to the framework preset
296+
(first match wins). The other lists are **unioned** with the preset.
297+
298+
### Suppressing a violation locally
299+
300+
```php
301+
/**
302+
* @phpquality-ignore solid.dip — this service intentionally wires concretes.
303+
*/
304+
final class FooService { /* … */ }
305+
```
306+
307+
Codes: `solid.srp`, `solid.dip`, `solid.isp`, `architecture.layer`.
308+
309+
### Baseline workflow (CI-friendly)
310+
311+
```bash
312+
# 1. Pin existing violations as accepted (do NOT fail the build)
313+
phpquality:analyze --source=app --type=laravel \
314+
--generate-baseline=phpquality.baseline.json
315+
316+
# 2. Subsequent runs only report NEW violations
317+
phpquality:analyze --source=app --type=laravel \
318+
--baseline=phpquality.baseline.json --fail-on-violation
319+
```
250320

251321
---
252322

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"license": "MIT",
66
"extra": {
77
"branch-alias": {
8-
"dev-dev": "1.6.x-dev"
8+
"dev-dev": "2.0.x-dev"
99
}
1010
},
1111
"keywords": [

0 commit comments

Comments
 (0)