# phpstan-doctrine **Repository Path**: mirrors_simPod/phpstan-doctrine ## Basic Information - **Project Name**: phpstan-doctrine - **Description**: Doctrine extensions for PHPStan - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: 2.0.x - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-06-29 - **Last Updated**: 2026-04-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Doctrine extensions for PHPStan [![Build](https://github.com/phpstan/phpstan-doctrine/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-doctrine/actions) [![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-doctrine/v/stable)](https://packagist.org/packages/phpstan/phpstan-doctrine) [![License](https://poser.pugx.org/phpstan/phpstan-doctrine/license)](https://packagist.org/packages/phpstan/phpstan-doctrine) * [PHPStan](https://phpstan.org/) * [Doctrine](https://www.doctrine-project.org/) This extension provides following features: * DQL validation for parse errors, unknown entity classes and unknown persistent fields. QueryBuilder validation is also supported. * Recognizes magic `findBy*`, `findOneBy*` and `countBy*` methods on EntityRepository. * Validates entity fields in repository `findBy`, `findBy*`, `findOneBy`, `findOneBy*`, `count` and `countBy*` method calls. * Interprets `EntityRepository` correctly in phpDocs for further type inference of methods called on the repository. * Provides correct return for `Doctrine\ORM\EntityManager::getRepository()`. * Provides correct return type for `Doctrine\ORM\EntityManager::find`, `getReference` and `getPartialReference` when `Foo::class` entity class name is provided as the first argument * Adds missing `matching` method on `Doctrine\Common\Collections\Collection`. This can be turned off by setting `parameters.doctrine.allCollectionsSelectable` to `false`. * Also supports Doctrine ODM. * Analysis of discrepancies between entity column types and property field types. This can be relaxed with the `allowNullablePropertyForRequiredField: true` setting. * Analysis of discrepancies between entity relation types and property field types (to-one, to-many). * Provides return type for `Doctrine\ORM\Query::getResult`, `getOneOrNullResult`, `getSingleResult`, `toIterable` and `execute` in `HYDRATE_OBJECT` mode (see below). * Reports `final` entity classes that can cause problems with Doctrine proxy generation (allowed when native lazy objects are enabled). * Reports `final` entity constructors that can cause problems with Doctrine proxy generation. * Detects Doctrine mapping configuration errors (annotation/attribute parsing issues). * Forbids direct use of Doctrine proxy class names. * Provides precise throw types for `EntityManager::flush()` (`ORMException`, `UniqueConstraintViolationException`). * Integrates with PHPStan dead code detection — entity properties are not reported as unused. Recognizes generated identifiers, version fields, and read-only entities as always written. * Supports [Gedmo doctrine-extensions](https://github.com/doctrine-extensions/DoctrineExtensions) — properties managed by Gedmo annotations/attributes (e.g. `Timestampable`, `Blameable`, `Slug`) are recognized for dead code detection. * Narrows `Collection::first()` and `Collection::last()` return types from `T|false` to `T` when `isEmpty()` is `false`. ## Installation To use this extension, require it in [Composer](https://getcomposer.org/): ```bash composer require --dev phpstan/phpstan-doctrine ``` If you also install [phpstan/extension-installer](https://github.com/phpstan/extension-installer) then you're all set!
Manual installation If you don't want to use `phpstan/extension-installer`, include extension.neon in your project's PHPStan config: ```neon includes: - vendor/phpstan/phpstan-doctrine/extension.neon ``` If you're interested in DQL/QueryBuilder validation, include also `rules.neon` (you will also need to provide the `objectManagerLoader`, see below): ```neon includes: - vendor/phpstan/phpstan-doctrine/rules.neon ```
## Configuration If your repositories have a common base class, you can configure it in your `phpstan.neon` and PHPStan will see additional methods you define in it: ```neon parameters: doctrine: ormRepositoryClass: MyApp\Doctrine\BetterEntityRepository odmRepositoryClass: MyApp\Doctrine\BetterDocumentRepository ``` You can opt in for more advanced analysis by providing the object manager from your own application. This will enable DQL validation: ```neon parameters: doctrine: objectManagerLoader: tests/object-manager.php ``` Example for Symfony 4: ```php // tests/object-manager.php use App\Kernel; require __DIR__ . '/../config/bootstrap.php'; $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $kernel->boot(); return $kernel->getContainer()->get('doctrine')->getManager(); ``` Example for Symfony 5: ```php // tests/object-manager.php use App\Kernel; use Symfony\Component\Dotenv\Dotenv; require __DIR__ . '/../vendor/autoload.php'; (new Dotenv())->bootEnv(__DIR__ . '/../.env'); $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $kernel->boot(); return $kernel->getContainer()->get('doctrine')->getManager(); ``` ## Query type inference This extension can infer the result type of DQL queries when an `objectManagerLoader` is provided. Examples: ```php $query = $entityManager->createQuery('SELECT u FROM Acme\User u'); $query->getResult(); // array $query = $entityManager->createQuery('SELECT u.id, u.email, u.name FROM Acme\User u'); $query->getResult(); // array $query = $entityManager->createQuery(' SELECT u.id, u.email, COALESCE(u.name, "Anonymous") AS name FROM Acme\User u '); $query->getSingleResult(Query::HYDRATE_OBJECT); // array{id: int, email: string, name: string}> $query = $entityManager->createQueryBuilder() ->select('u') ->from(User::class, 'u') ->getQuery(); $query->getResult(); // array ``` Queries are analyzed statically and do not require a running database server. This makes use of the Doctrine DQL parser and entities metadata. Most DQL features are supported, including `GROUP BY`, `INDEX BY`, `DISTINCT`, all flavors of `JOIN`, arithmetic expressions, functions, aggregations, `NEW`, etc. Sub queries are not yet supported (infered type will be `mixed`). ### Query type inference of expressions Whether e.g. `SUM(e.column)` is fetched as `float`, `numeric-string` or `int` highly [depends on drivers, their setup and PHP version](https://github.com/janedbal/php-database-drivers-fetch-test). This extension autodetects your setup and provides quite accurate results for `pdo_mysql`, `mysqli`, `pdo_sqlite`, `sqlite3`, `pdo_pgsql` and `pgsql`. ### Supported methods The `getResult` method is supported when called without argument, or with the hydrateMode argument set to `Query::HYDRATE_OBJECT`: ``` php $query = $entityManager->createQuery('SELECT u FROM Acme\User u'); $query->getResult(); // array $query->getResult(Query::HYDRATE_OBJECT); // array ``` The methods `getOneOrNullResult`, `getSingleResult`, `toIterable`, and `execute` are supported when the hydrateMode argument is explicitly set to `Query::HYDRATE_OBJECT`: ``` php $query = $entityManager->createQuery('SELECT u FROM Acme\User u'); $query->getOneOrNullResult(); // mixed $query->getOneOrNullResult(Query::HYDRATE_OBJECT); // User ``` This is due to the design of the `Query` class preventing from determining the hydration mode used by these functions unless it is specified explicitly during the call. ### Problematic approaches Not every QueryBuilder can be statically analysed, here are few advices to maximize type inferring: - Do not pass QueryBuilder to methods - Do not use dynamic expressions in QueryBuilder methods (mainly in `select`/`join`/`from`/`set`) You can enable reporting of places where inferring is unavailable by: ```neon parameters: doctrine: reportDynamicQueryBuilders: true ``` ## Custom types If your application uses custom Doctrine types, you can write your own type descriptors to analyse them properly. Type descriptors implement the interface `PHPStan\Type\Doctrine\Descriptors\DoctrineTypeDescriptor` which looks like this: ```php walkSimpleArithmeticExpression($this->arithmeticExpression) . ')'; } public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); $this->arithmeticExpression = $parser->SimpleArithmeticExpression(); $parser->match(TokenType::T_CLOSE_PARENTHESIS); } public function getReturnType(): Type { return Type::getType(Types::INTEGER); } } ``` ## Literal strings Stub files in phpstan-doctrine come with many parameters marked with `literal-string`. This is a security-focused type that only allows literal strings written in code to be passed into these parameters. This reduces risk of SQL injection because dynamic strings from user input are not accepted in place of `literal-string`. An example where this type is used is `$sql` parameter in `Doctrine\Dbal\Connection::executeQuery()`. To enable this advanced type in phpstan-doctrine, use this configuration parameter: ```neon parameters: doctrine: literalString: true ```