Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 93 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,97 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.0.0] - 2026-05-06

Major release focused on **dramatically reducing false positives** in
architectural / SOLID analysis on framework projects (Laravel and Symfony).
The headline change is that `--type=laravel` is now a real, opinionated preset
instead of a near-no-op flag.

### Breaking changes

- **Layer classification is now rule-based.** The previous
`(layer ⇒ namespace[] + suffix[])` lookup is replaced by an ordered list of
glob `LayerRule` objects (first match wins). `array_merge_recursive` is gone.
Custom `ProjectTypeInterface` implementations must implement the new
`getLayerRules()` method (defaults provided in `AbstractProjectType`).
- **`*Action` is no longer classified as `Controller` in Laravel.** It maps to
`Application` (use case). This eliminates ~12 spurious
"Infrastructure → Controller" violations on a typical Laravel app using the
Action pattern.
- **`Wiring` is now a layer.** `ServiceProvider`, container extensions and
files under `**\\Providers\\**` are flagged as `Wiring`, exempt from layer
violation reporting and from DIP scoring. Their job is precisely to bind
concrete classes from any layer, so the previous violations were noise.
- **DIP scope reduced to injected dependencies.** Only `type_hint_param`,
`type_hint_return`, and `type_hint_property` count toward the abstraction
ratio. `extends`, `trait`, `new`, `static_call`, `instanceof`, `catch`,
`implements`, `const`, `use` no longer pollute the metric. Old DIP scores
are not comparable.
- **DIP whitelist by default.** The Laravel preset excludes Eloquent
primitives, Carbon, Closure, facades, queue/bus traits, and OpenAPI
attributes from the DIP ratio. The Symfony preset excludes Doctrine ORM,
HttpFoundation, Form, Validator, Security, Twig, etc. This eliminates the
pathological case of e.g. `OrganizationController` flagged with
`104/104 concrete dependencies`.
- **`ProjectTypeInterface` gains 3 methods**: `getLayerRules()`,
`getDipIgnoreList()`, `getWiringPatterns()`. `AbstractProjectType` provides
generic defaults so existing custom types still compile, but presets that
want framework-aware behaviour must override them.
- **`SolidAnalyzer::analyze()` and `ArchitectureAnalyzer::analyze()` signatures
changed.** `SolidAnalyzer::analyze(array $fileResults, array $dipIgnoreList = [], array $wiringClasses = [], array $classIgnores = [])`; `ArchitectureAnalyzer::analyze(array $fileResults, ?ProjectTypeInterface $projectType = null, ?ProjectConfig $config = null)`.
- **`ProjectAnalyzer` constructor now requires `ProjectConfigLoader` and
`BaselineManager`.** Symfony autowiring picks them up automatically.

### Added

- **`phpquality.json` project configuration file** at the project root.
Schema (all keys optional):
```json
{
"layers": { "rules": [{ "match": "App\\Foo\\**", "layer": "Application" }] },
"wiring": { "patterns": ["**ServiceProvider"] },
"abstractionRatio": { "ignore": ["App\\Models\\**"] },
"ignore": { "violations": ["solid.dip:App\\Foo\\Bar"] },
"baseline": "phpquality.baseline.json"
}
```
`layers.rules` is **prepended** to the framework preset (project rules win).
`wiring.patterns`, `abstractionRatio.ignore`, `ignore.violations` are
**unioned** with framework defaults.
- **`@phpquality-ignore` docblock annotation** on classes and interfaces:
```php
/** @phpquality-ignore solid.dip — wiring intentionnel */
final class FooService { … }
```
Codes: `solid.srp`, `solid.dip`, `solid.isp`, `architecture.layer`.
- **Baseline generation and application**.
- `--generate-baseline=phpquality.baseline.json` writes a hash-keyed list of
all current violations and exits successfully (does not fail-on-violation).
- `--baseline=phpquality.baseline.json` filters violations whose hash is in
the baseline. The summary exposes `suppressedByBaseline` and warns about
`obsoleteBaselineEntries` (entries that no longer match anything →
regenerate).
- New CLI options: `--config`, `--baseline`, `--generate-baseline`.
- New services: `PhpQuality\Config\ProjectConfigLoader`,
`PhpQuality\Config\BaselineManager`, `PhpQuality\Config\ProjectConfig`,
`PhpQuality\Config\LayerRule`,
`PhpQuality\Analyzer\Ast\IgnoreAnnotationParser`.

### Migrating from 1.x

Most users only need to run the new version: false positives drop on their
own. If you previously had a CI policy on the violation counts, expect those
counts to **decrease**.

If you have a custom `ProjectTypeInterface` implementation:
1. Have it extend `AbstractProjectType` (gets the 3 new methods for free), OR
2. Implement `getLayerRules()`, `getDipIgnoreList()`, `getWiringPatterns()`.

If you want to keep some violations in your reports while you migrate, pin
them via `--generate-baseline` and commit the baseline file. Subsequent runs
with `--baseline=…` will only show new violations.

## [1.6.0] - 2026-03-26

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

[Unreleased]: https://github.com/amoifr/PhpQuality/compare/v1.6.0...HEAD
[Unreleased]: https://github.com/amoifr/PhpQuality/compare/v2.0.0...HEAD
[2.0.0]: https://github.com/amoifr/PhpQuality/compare/v1.6.0...v2.0.0
[1.6.0]: https://github.com/amoifr/PhpQuality/compare/v1.5.0...v1.6.0
[1.5.0]: https://github.com/amoifr/PhpQuality/compare/v1.4.1...v1.5.0
[1.4.1]: https://github.com/amoifr/PhpQuality/compare/v1.4.0...v1.4.1
Expand Down
19 changes: 19 additions & 0 deletions DOCKERHUB.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ docker run --rm \
| `--fail-on-violation` | Exit with error if violations found (CI mode) |
| `--list-types` | List available project types |
| `--list-langs` | List available languages |
| `--config` | Path to a project configuration file (default: `./phpquality.json`) |
| `--baseline` | Filter out violations listed in the baseline file |
| `--generate-baseline` | Write a baseline of current violations and exit successfully |

---

## What's new in 2.0

- The Laravel preset (`--type=laravel`) is now a real preset: `*Action` is
classified as `Application`, `ServiceProvider` as `Wiring` (exempt from
layer violations), and Eloquent / Carbon / facades / queue traits are
excluded from the DIP ratio.
- DIP only counts type-hinted, injectable dependencies — no more false
positives from `use Trait;` or PHP attributes.
- New `phpquality.json` for project-level overrides, `@phpquality-ignore`
docblock annotation, and a `--baseline` workflow to adopt the tool on
existing projects without rewriting them.

See [CHANGELOG.md](https://github.com/Amoifr/phpquality/blob/main/CHANGELOG.md) for the full migration guide.

---

Expand Down
72 changes: 71 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,33 @@
> 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:** 1.2.0
**Version:** 2.0.0
**Author:** [Pascal CESCON](https://moi.ruedesjasses.fr)
**GitHub:** [amoifr/PhpQuality](https://github.com/amoifr/PhpQuality)

---

## Migrating from 1.x

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: `*Action` is
`Application` (not Controller), `ServiceProvider` is `Wiring` (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.json` file at the project root can override layer rules,
add to whitelists, and pin a baseline.
- `--generate-baseline=phpquality.baseline.json` then `--baseline=…` lets
you adopt the tool on existing projects without rewriting them.

See [`CHANGELOG.md`](CHANGELOG.md) for the full breaking changes list and
detailed migration notes.

---

## Installation

### As a Symfony Bundle
Expand Down Expand Up @@ -247,6 +268,55 @@ docker run --rm \
| `--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. |

### Project configuration file (`phpquality.json`, since 2.0)

Drop a `phpquality.json` at the root of the project being analyzed to extend
the framework preset:

```json
{
"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.

### Suppressing a violation locally

```php
/**
* @phpquality-ignore solid.dip — this service intentionally wires concretes.
*/
final class FooService { /* … */ }
```

Codes: `solid.srp`, `solid.dip`, `solid.isp`, `architecture.layer`.

### Baseline workflow (CI-friendly)

```bash
# 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-violation
```

---

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "MIT",
"extra": {
"branch-alias": {
"dev-dev": "1.6.x-dev"
"dev-dev": "2.0.x-dev"
}
},
"keywords": [
Expand Down
Loading
Loading