phpstan/phpstan-doctrine
PHPStan extension for Doctrine that validates DQL and QueryBuilder, checks repository findBy and magic methods, improves EntityManager and repository type inference, supports ORM and ODM, and reports mapping, entity type, and proxy issues.
getRepository(), magic findBy* methods, and DQL results otherwise degrade to mixed and generate noise. This package provides the type inference that makes a level upgrade a tractable roadmap item rather than an open-ended cleanup.findBy*/findOneBy*/countBy* move a class of data-access bugs from production 500s into pre-merge failures. That supports a decision to treat CI as the primary correctness gate for query construction.doctrine.literalString setting types DBAL parameters (e.g. Connection::executeQuery($sql)) as literal-string, so interpolated user input is flagged statically. This is a concrete, low-cost control to cite in secure-coding standards and audit responses.select/from/join), and reportDynamicQueryBuilders: true turns the convention into an enforceable signal.final entity classes and final constructors that break Doctrine proxy generation, plus mapping configuration errors, directly de-risks Doctrine upgrades and a migration to native lazy objects. Precise throw types for EntityManager::flush() also improve error-handling design around uniqueness violations.Timestampable, Blameable, Slug), generated identifiers, and version fields are recognized. This keeps a cleanup initiative from drowning in false positives on the domain model.reportUnknownTypes: true converts "we have some custom DBAL types" into an explicit checklist, letting you size descriptor work (or lean on ReflectionDescriptor) before committing to full analysis coverage.Good fit:
objectManagerLoader bootstrap (kernel boot, env loading, container lookup), including per-kernel variants in multi-app monorepos.reportUnknownTypes flags them.Look elsewhere or defer:
mixed.Query methods only resolve types when Query::HYDRATE_OBJECT is passed explicitly.For executives. A free, MIT-licensed, development-only Composer dependency, published under the official phpstan/ organization with a steady release cadence (2.0.x line, active maintenance). It converts a recurring category of production incidents — misspelled fields in repository findBy* calls, malformed or referencing-nonexistent-field DQL, Doctrine mapping mistakes, and proxy failures from final entities — into build-time errors. There is no runtime footprint, no license cost, and no vendor lock-in: adoption is one dev dependency plus a config include, and rollback is deleting a line from phpstan.neon. Setup and initial triage is on the order of days, not a quarter.
For engineering. Value arrives in stages: install the extension for immediate type inference (EntityManager::getRepository(), find, getReference, magic findBy*/countBy*, EntityRepository<T> generics, Collection::first()/last() narrowing); then add rules.neon plus an objectManagerLoader to unlock DQL validation and full query result-type inference (including driver-aware expression types for MySQL, SQLite, and PostgreSQL setups). From there, opt into literalString for injection-risk detection, reportUnknownTypes to close custom-type coverage gaps, and reportDynamicQueryBuilders to surface code that static analysis cannot reason about. Custom DQL functions that implement TypedExpression are inferred correctly; others fall back to mixed.
What to measure. DQL and repository-field errors caught pre-merge, PHPStan level achieved, reduction in data-layer runtime exceptions, and time-to-diagnose during ORM upgrades. Risks to name up front: an initial baseline of findings, ongoing ownership of the object-manager bootstrap, occasional false positives around proxy-sensitive final classes (relaxable, and moot once native lazy objects are enabled), and the need to author descriptors for custom DBAL types.
How can I help you explore Laravel packages today?