4

I am using PHPStan with its Doctrine extension.

I have a custom entity repository called App\Repository\Doctrine\UserRepository with the @extends doc block:

/**
 * @extends \Doctrine\ORM\EntityRepository<\App\Entity\User>
 */
class UserRepository extends EntityRepository implements IUserRepository
{
    public function customRepositoryMethod()
    {
        // ...
    }
}

In a controller, this code:

    public function getUserMatches(EntityManager $em)
    {
        $userRepo = $em->getRepository(\App\Entity\User::class);
        $userRepo->customRepositoryMethod();
    }

...results in this PHPStan error:

Call to an undefined method Doctrine\ORM\EntityRepository<meQ\Entity\User>::customRepositoryMethod().

Thanks to phpstan-doctrine, static analysis knows that $em->getRepository(User::class) returns a EntityRepository<User>.

However, it does not know to consider the custom repository class UserRepository as the implementation of that generic type.

How do I DocBlock the UserRepository class, or otherwise configure PHPStan, so that it interprets UserRepository as the implementation of EntityRepository<User>?

What else I've tried

I've also tried this DocBlock on UserRepository to no avail:

/**
 * @implements \Doctrine\ORM\EntityRepository<\App\Entity\User>
 */
yivi
  • 42,438
  • 18
  • 116
  • 138
amacrobert
  • 2,707
  • 2
  • 28
  • 37

1 Answers1

4

PhpStan has no way of knowing EntityManager::getRepository() is going to return your custom method.

Either add a @var annotation right there:

/** @var UserRepository $userRepo */
$userRepo = $em->getRepository(\App\Entity\User::class);

Or better yet, just inject the UserRepository, not the whole EntityManager:

public function getUserMatches(UserRepository $userRepository)
{
   $userRepository->customRepositoryMethod();
}

The above would work with PhpStan or any static analysis tool out of the box. (And, injecting the specific repository instead of the Entity Manager is a better practice in any case).

But if not, you can always try installing the Doctrine Extensions for PHPStan, which may help the tool understand the codebase in relationship to Doctrine ORM.

If you are already using the Doctrine Extensions, note that from what I gather in the docs it's only able to extract typing if your mapping configuration is provided via annotations. If you configurate ORM mappings via XML (or YAML, in older versions of Doctrine), then I think the Extensions won't be able to extract the typing data, and the above solutions will be your only way to go.

yivi
  • 42,438
  • 18
  • 116
  • 138
  • 2
    I am with @harvident here. According to the [PHPStan Doctrine documentation](https://github.com/phpstan/phpstan-doctrine), `Provides correct return for Doctrine\ORM\EntityManager::getRepository()`. Which is perfectly reasonable for an extension to do. I'm guessing the extension is not loaded correctly. – Cerad Mar 21 '22 at 17:26
  • Thanks for the response. Further explanation: (1) Using @var to annotate the repo variables is what I was originally doing, but it can be annoying to do everywhere. (2) Injecting custom repositories as services is great, but isn't as straightforward as this answer makes it out (at least not in Symfony 4.4) -- and not possible for non-custom repos without more config. The original question is specifically about PHPStan's Doctrine extension -- I installed it to mitigate both (1) and (2), which its documentation makes sound possible. – amacrobert Mar 22 '22 at 02:27
  • 1
    Sorry @amacrobert, I originally missed that you were using the Doctrine Extensions for PhpStan. If you are, then the question is missing the ORM mapping configuration. I don't think PhpStan will be able to extract typing information from YAML or XML configuration. Injecting custom repositories is equally straightforward on Synfony 4.4. Sure, it's nos possible for cases where you are using the `EntityRepository` directly... but again, I don't believe that's a great practice. I rather create the code and let the code explain itself, to rely on some magic inference. – yivi Mar 22 '22 at 09:16