PHPackages                             phpstan/phpstan-doctrine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. phpstan/phpstan-doctrine

ActivePhpstan-extension[Testing &amp; Quality](/categories/testing)

phpstan/phpstan-doctrine
========================

Doctrine extensions for PHPStan

2.0.20(2mo ago)66766.6M—6.2%118[78 issues](https://github.com/phpstan/phpstan-doctrine/issues)[18 PRs](https://github.com/phpstan/phpstan-doctrine/pulls)20MITPHPPHP ^7.4 || ^8.0CI passing

Since Jan 30Pushed today7 watchersCompare

[ Source](https://github.com/phpstan/phpstan-doctrine)[ Packagist](https://packagist.org/packages/phpstan/phpstan-doctrine)[ RSS](/packages/phpstan-phpstan-doctrine/feed)WikiDiscussions 2.0.x Synced 1mo ago

READMEChangelog (10)Dependencies (44)Versions (209)Used By (20)

Doctrine extensions for PHPStan
===============================

[](#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://camo.githubusercontent.com/deaa4f6d1fce5370bcf5a4f8825bf3a377b8002b629e82ec1a10b8928331b030/68747470733a2f2f706f7365722e707567782e6f72672f7068707374616e2f7068707374616e2d646f637472696e652f762f737461626c65)](https://packagist.org/packages/phpstan/phpstan-doctrine)[![License](https://camo.githubusercontent.com/dabfce1f3fe6b6453f151e6db43faf2c8c1524a7cb5c00c84d445f9a99e0fc44/68747470733a2f2f706f7365722e707567782e6f72672f7068707374616e2f7068707374616e2d646f637472696e652f6c6963656e7365)](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
------------

[](#installation)

To use this extension, require it in [Composer](https://getcomposer.org/):

```
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 installationIf you don't want to use `phpstan/extension-installer`, include extension.neon in your project's PHPStan config:

```
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):

```
includes:
    - vendor/phpstan/phpstan-doctrine/rules.neon
```

Configuration
-------------

[](#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:

```
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:

```
parameters:
	doctrine:
		objectManagerLoader: tests/object-manager.php
```

Example for Symfony 4:

```
// 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:

```
// 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
--------------------

[](#query-type-inference)

This extension can infer the result type of DQL queries when an `objectManagerLoader` is provided.

Examples:

```
$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

[](#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

[](#supported-methods)

The `getResult` method is supported when called without argument, or with the hydrateMode argument set to `Query::HYDRATE_OBJECT`:

```
$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`:

```
$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

[](#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:

```
parameters:
	doctrine:
		reportDynamicQueryBuilders: true
```

Custom types
------------

[](#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:

```
