# Code Quality Scanner This module provides repository-aware rules that validate architectural conventions. It currently ships with the following rules: - `StringableImplementation`: classes that define `__toString()` must implement `Stringable` - `FinalClass`: value-object classes must be declared `final` - `ReadonlyClass`: value-object classes must be declared `readonly` - `ReadonlyGetter`: readonly properties (or classes) should be exposed directly; avoid `get*()` wrappers - `NoInheritance`: inheritance is forbidden (except for a small allow list, e.g. Exceptions) ## Command Line Usage Run the scanner from the project root: ```bash php console.php quality:scan # scans src/ by default php console.php quality:scan app/path # scan a custom directory php console.php quality:scan --skip-errors # skip classes/files that cannot be parsed ``` The command groups violations by rule and shows file locations: ``` ❌ Found 2 violation(s). Scanned 185 files, 174 classes in 6.91s StringableImplementation (2) • App\Domain\User\ValueObjects\UserId ↳ src/Domain/User/ValueObjects/UserId.php:42 Classes defining __toString() must implement Stringable explicitly. ``` The exit code is `0` when all checks pass, or `1` when violations are found. You can wire this command into CI to block regressions. ### Skipping Faulty Classes Some parts of the repository (legacy examples, experimental prototypes, etc.) may not load because they contain invalid PHP constructs or depend on unavailable interfaces. Use the `--skip-errors` flag to collect a warning and continue scanning the remaining classes: ```bash php console.php quality:scan --skip-errors ``` When the flag is omitted, any parsing/reflection error stops the scan and the command exits with a non-zero status. ## Adding New Rules Rules implement `App\Framework\Quality\CodeQuality\CodeQualityRule`: ```php use App\Framework\Quality\CodeQuality\CodeQualityRule; use App\Framework\Quality\CodeQuality\Results\RuleViolation; use App\Framework\Quality\CodeQuality\ValueObjects\ClassInspection; final class MyCustomRule implements CodeQualityRule { public function name(): string { return 'MyCustomRule'; } public function check(ClassInspection $inspection): array { if ($this->isValid($inspection)) { return []; } $line = $inspection->getMethodLine('someMethod'); return [ RuleViolation::create( $inspection->className(), $inspection->filePath(), $this->name(), 'Explain what must change.', $line ), ]; } } ``` Register the rule inside `CodeQualityScanCommand`: ```php $this->scanner = new CodeQualityScanner( $fileScanner, $reflectionService, [ new RequireStringableImplementationRule(), new MyCustomRule(), ], $logger ); ``` Because rules leverage full reflection data and the actual file path, you have the flexibility to enforce namespacing, attribute usage, dependency boundaries, and more. ## Storage Permissions The scanner runs inside the console bootstrap. When Redis is unavailable, the framework falls back to the filesystem cache. The cache directory now resolves to `/storage/cache`, but you still need to make sure it exists and is writable: ```bash mkdir -p storage/cache chmod -R ug+rw storage ``` If you run the command from another working directory (e.g. inside Docker), set the base path explicitly so the cache resolver finds the right storage folder: ```bash APP_BASE_PATH=/home/michael/dev/michaelschiemer php console.php quality:scan ``` The CLI entry point already exports `APP_BASE_PATH`; keep the environment variable handy for custom runners, background workers, or integration tests.