|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance for AI agents working on the phpstan/phpstan-doctrine repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +phpstan-doctrine is a PHPStan extension that provides static analysis capabilities for Doctrine ORM, DBAL, and ODM. It adds type inference, DQL validation, QueryBuilder analysis, and custom rules that detect Doctrine-specific bugs at analysis time without running the application. |
| 8 | + |
| 9 | +Key features: |
| 10 | +- DQL query validation (parse errors, unknown entities, unknown fields) |
| 11 | +- QueryBuilder type inference and validation |
| 12 | +- Magic repository method recognition (`findBy*`, `findOneBy*`, `countBy*`) |
| 13 | +- Entity column/relation type checking against property types |
| 14 | +- Return type inference for `EntityManager::getRepository()`, `find()`, `getReference()` |
| 15 | +- Query result type inference for `getResult()`, `getSingleResult()`, etc. |
| 16 | +- Doctrine ODM support |
| 17 | +- Database driver-aware type resolution for expressions like `SUM()`, `AVG()` |
| 18 | + |
| 19 | +## Repository Structure |
| 20 | + |
| 21 | +``` |
| 22 | +src/ # Extension source code (PSR-4: PHPStan\) |
| 23 | +├── Classes/ # Forbidden class name extensions (proxy detection) |
| 24 | +├── Doctrine/ # Core Doctrine integration (driver detection, metadata loading) |
| 25 | +├── PhpDoc/ # PHPDoc type node resolver extensions |
| 26 | +├── Reflection/ # Class reflection extensions (repository methods, selectable) |
| 27 | +├── Rules/ # Custom PHPStan rules (entity validation, DQL checks) |
| 28 | +│ ├── Doctrine/ORM/ # ORM-specific rules |
| 29 | +│ └── Gedmo/ # Gedmo doctrine-extensions support |
| 30 | +├── Stubs/ # Stub file loader |
| 31 | +└── Type/ # Type inference extensions |
| 32 | + └── Doctrine/ |
| 33 | + ├── Collection/ # Collection type narrowing |
| 34 | + ├── DBAL/ # DBAL QueryBuilder and Result types |
| 35 | + ├── Descriptors/# Doctrine type → PHPStan type mappings (28 descriptors) |
| 36 | + ├── Query/ # DQL Query result type walker and inference |
| 37 | + └── QueryBuilder/ # ORM QueryBuilder type tracking |
| 38 | +tests/ # Test suite |
| 39 | +├── DoctrineIntegration/# Integration tests (ORM, ODM, Persistence) |
| 40 | +├── Platform/ # Database platform tests (MySQL, PostgreSQL, SQLite) |
| 41 | +├── Reflection/ # Reflection extension tests |
| 42 | +├── Rules/ # Rule tests (entity validation, dead code, properties) |
| 43 | +└── Type/ # Type inference tests |
| 44 | +stubs/ # PHPStan stub files for Doctrine classes |
| 45 | +├── Collections/ # Collection and Selectable stubs |
| 46 | +├── DBAL/ # DBAL types, cache, exception stubs |
| 47 | +├── ORM/ # ORM Query, QueryBuilder, Mapping stubs |
| 48 | +├── Persistence/ # Persistence layer stubs |
| 49 | +└── runtime/Enum/ # PHP 8.1 enum polyfill stubs |
| 50 | +compatibility/ # Compatibility layer for multiple Doctrine versions |
| 51 | +├── patches/ # Composer patches for ORM v3 attribute support |
| 52 | +├── AnnotationDriver.php # ORM v2 fallback |
| 53 | +├── ArrayType.php # DBAL v3 fallback |
| 54 | +└── orm-3-baseline.php # Dynamic baseline selection by ORM version |
| 55 | +``` |
| 56 | + |
| 57 | +## PHP Version Support |
| 58 | + |
| 59 | +This repository supports **PHP 7.4+**. All source code in `src/` and `tests/` must be compatible with PHP 7.4. Do not use language features from PHP 8.0+ (named arguments, union types in signatures, match expressions, etc.) in the main source code. |
| 60 | + |
| 61 | +Some test data files under `tests/*/data-php-*` and `tests/*/data-attributes` target specific PHP versions and are excluded from lint/analysis on older versions. |
| 62 | + |
| 63 | +## Multi-Version Doctrine Support |
| 64 | + |
| 65 | +The extension supports multiple major versions of Doctrine libraries simultaneously: |
| 66 | +- **Doctrine ORM**: 2.x and 3.x |
| 67 | +- **Doctrine DBAL**: 3.x and 4.x |
| 68 | +- **Doctrine ODM**: 2.4+ |
| 69 | +- **Doctrine Persistence**: 2.x and 3.x |
| 70 | + |
| 71 | +This is achieved through: |
| 72 | +- The `compatibility/` directory providing fallback classes for missing APIs |
| 73 | +- Composer patches in `compatibility/patches/` for ORM v3 attribute support |
| 74 | +- Version-specific PHPStan baselines (`phpstan-baseline-orm-2.neon`, `phpstan-baseline-orm-3.neon`, `phpstan-baseline-dbal-3.neon`, `phpstan-baseline-dbal-4.neon`) |
| 75 | +- Dynamic baseline selection in `compatibility/orm-3-baseline.php` |
| 76 | +- `method_exists()` checks in source code for version-dependent API calls |
| 77 | +- CI matrix testing across version combinations |
| 78 | + |
| 79 | +## Configuration Files |
| 80 | + |
| 81 | +- **`extension.neon`** — Main extension services: type descriptors, dynamic return type extensions, reflection extensions, PHPDoc resolvers. Loaded automatically via phpstan/extension-installer. |
| 82 | +- **`rules.neon`** — Custom validation rules (DQL, entity columns, relations, mapping). Optionally included by users who provide an `objectManagerLoader`. |
| 83 | +- **`phpstan.neon`** — Self-analysis configuration (level 8, includes all baselines and strict rules). |
| 84 | +- **`phpunit.xml`** — Test configuration. The `platform` test group is excluded by default and runs separately in CI. |
| 85 | + |
| 86 | +## Development Commands |
| 87 | + |
| 88 | +All commands are defined in the `Makefile`: |
| 89 | + |
| 90 | +```bash |
| 91 | +make check # Run all checks (lint, cs, tests, phpstan) |
| 92 | +make tests # Run PHPUnit tests |
| 93 | +make lint # Run parallel-lint on src/ and tests/ |
| 94 | +make cs # Run coding standard checks (requires make cs-install first) |
| 95 | +make cs-fix # Auto-fix coding standard violations |
| 96 | +make cs-install # Clone phpstan/build-cs repository |
| 97 | +make phpstan # Run PHPStan self-analysis |
| 98 | +make phpstan-generate-baseline # Regenerate the PHPStan baseline |
| 99 | +``` |
| 100 | + |
| 101 | +## Running Tests |
| 102 | + |
| 103 | +```bash |
| 104 | +composer install |
| 105 | +make tests # Runs: php vendor/bin/phpunit |
| 106 | +``` |
| 107 | + |
| 108 | +Platform tests (requiring database containers) are in the `platform` group and excluded by default. They run in CI via `.github/workflows/platform-test.yml` with MySQL and PostgreSQL services. |
| 109 | + |
| 110 | +## Coding Standards |
| 111 | + |
| 112 | +The project uses the [phpstan/build-cs](https://github.com/phpstan/build-cs) coding standard (branch `2.x`): |
| 113 | + |
| 114 | +```bash |
| 115 | +make cs-install # Clone the build-cs repo |
| 116 | +make cs # Check coding standards |
| 117 | +make cs-fix # Auto-fix violations |
| 118 | +``` |
| 119 | + |
| 120 | +Key style rules: |
| 121 | +- Tab indentation for PHP, XML, and NEON files |
| 122 | +- Space indentation (2 spaces) for YAML files |
| 123 | +- LF line endings, UTF-8 encoding |
| 124 | +- Trailing whitespace trimmed, final newline required |
| 125 | + |
| 126 | +## CI Pipeline |
| 127 | + |
| 128 | +The CI runs on every pull request and push to `2.0.x`: |
| 129 | + |
| 130 | +1. **Lint** — `parallel-lint` on PHP 7.4–8.4 |
| 131 | +2. **Coding Standard** — phpcs with build-cs rules |
| 132 | +3. **Tests** — PHPUnit on PHP 7.4–8.4, both lowest and highest dependencies, plus a special matrix entry with ORM 3.x + DBAL 4.x |
| 133 | +4. **Static Analysis** — PHPStan at level 8 on PHP 7.4–8.4, plus ORM 3.x + DBAL 4.x variant |
| 134 | +5. **Mutation Testing** — Infection on PHP 8.2–8.4, requires 100% MSI on changed lines |
| 135 | +6. **Platform Tests** — PHPUnit `platform` group against real MySQL and PostgreSQL databases |
| 136 | + |
| 137 | +## Architecture Notes |
| 138 | + |
| 139 | +### Type Descriptors |
| 140 | + |
| 141 | +Type descriptors (`src/Type/Doctrine/Descriptors/`) map Doctrine DBAL column types to PHPStan types. Each descriptor implements `DoctrineTypeDescriptor` with: |
| 142 | +- `getType()` — Returns the Doctrine type class name |
| 143 | +- `getWritableToPropertyType()` — The PHP type Doctrine writes to entity properties |
| 144 | +- `getWritableToDatabaseType()` — The PHP type that can be written to the database |
| 145 | + |
| 146 | +Descriptors are registered as services tagged with `phpstan.doctrine.typeDescriptor` in `extension.neon`. |
| 147 | + |
| 148 | +### Query Result Type Walker |
| 149 | + |
| 150 | +`QueryResultTypeWalker` (`src/Type/Doctrine/Query/`) walks the DQL AST to infer result types. It handles SELECT expressions, JOINs, aggregations, NEW expressions, and INDEX BY. It is driver-aware — result types for expressions like `SUM()` or `AVG()` depend on the database driver (MySQL vs PostgreSQL vs SQLite). |
| 151 | + |
| 152 | +### QueryBuilder Type Tracking |
| 153 | + |
| 154 | +The extension tracks QueryBuilder method calls through `QueryBuilderType`, carrying DQL parts as PHPStan type metadata. When `getQuery()` is called, it reconstructs the DQL and runs it through the type walker. |
| 155 | + |
| 156 | +### Custom Rules |
| 157 | + |
| 158 | +Rules in `src/Rules/Doctrine/ORM/` validate entity mappings at analysis time: |
| 159 | +- `EntityColumnRule` — Column types match property types |
| 160 | +- `EntityRelationRule` — Relation configurations are valid |
| 161 | +- `EntityNotFinalRule` — Entities aren't `final` (breaks proxy generation) |
| 162 | +- `DqlRule` / `QueryBuilderDqlRule` — DQL syntax and semantic validation |
| 163 | +- `EntityMappingExceptionRule` — Catches mapping configuration errors |
| 164 | + |
| 165 | +### Stubs |
| 166 | + |
| 167 | +Stub files in `stubs/` provide PHPStan with type information for Doctrine classes, adding generics (`@template T of object`) and narrowed return types that the original Doctrine source doesn't express. These are loaded via `StubFilesExtensionLoader`. |
| 168 | + |
| 169 | +## Branching |
| 170 | + |
| 171 | +- **`2.0.x`** — Development branch (default for PRs) |
| 172 | +- **`main`** — Main/release branch |
0 commit comments