diff --git a/extension.neon b/extension.neon index 13dba828..97434782 100644 --- a/extension.neon +++ b/extension.neon @@ -11,3 +11,15 @@ services: class: PHPStan\Type\Doctrine\EntityManagerFindDynamicReturnTypeExtension tags: - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: PHPStan\Type\Doctrine\ObjectManagerGetRepositoryDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: PHPStan\Type\Doctrine\ManagerRegistryGetRepositoryDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: Doctrine\Common\Annotations\AnnotationReader + setup: + - Doctrine\Common\Annotations\AnnotationRegistry::registerLoader( class_exists ) diff --git a/src/Type/Doctrine/AbstractGetRepositoryDynamicReturnTypeExtension.php b/src/Type/Doctrine/AbstractGetRepositoryDynamicReturnTypeExtension.php new file mode 100644 index 00000000..f5175db5 --- /dev/null +++ b/src/Type/Doctrine/AbstractGetRepositoryDynamicReturnTypeExtension.php @@ -0,0 +1,106 @@ +annotationReader = $annotationReader; + } + + public function setBroker(\PHPStan\Broker\Broker $broker) + { + $this->broker = $broker; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return in_array($methodReflection->getName(), [ + 'getRepository', + ], true); + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + if (count($methodCall->args) === 0) { + return $methodReflection->getReturnType(); + } + $arg = $methodCall->args[0]->value; + if (!($arg instanceof \PhpParser\Node\Expr\ClassConstFetch)) { + return $methodReflection->getReturnType(); + } + + $class = $arg->class; + if (!($class instanceof \PhpParser\Node\Name)) { + return $methodReflection->getReturnType(); + } + + $class = (string) $class; + + if ($class === 'static') { + return $methodReflection->getReturnType(); + } + + if ($class === 'self') { + $class = $scope->getClassReflection()->getName(); + } + + if (!$this->broker->hasClass($class)) { + return $methodReflection->getReturnType(); + } + + $classReflection = $this->broker->getClass($class); + $annotations = $this->annotationReader + ->getClassAnnotations($classReflection->getNativeReflection()); + + /** @var \Doctrine\ORM\Mapping\Entity|null $entityAnnotation */ + $entityAnnotation = null; + foreach ($annotations as $annotation) { + if ($annotation instanceof Entity) { + $entityAnnotation = $annotation; + } + } + + if ($entityAnnotation === null) { + return $methodReflection->getReturnType(); + } + + $repositoryClass = EntityRepository::class; + if ( + $entityAnnotation->repositoryClass && + $this->broker->hasClass($entityAnnotation->repositoryClass) + ) { + $repositoryClass = $entityAnnotation->repositoryClass; + } + + return new ObjectType($repositoryClass); + } + +} diff --git a/src/Type/Doctrine/ManagerRegistryGetRepositoryDynamicReturnTypeExtension.php b/src/Type/Doctrine/ManagerRegistryGetRepositoryDynamicReturnTypeExtension.php new file mode 100644 index 00000000..32ede55e --- /dev/null +++ b/src/Type/Doctrine/ManagerRegistryGetRepositoryDynamicReturnTypeExtension.php @@ -0,0 +1,13 @@ +