Skip to content

Commit 2910fcc

Browse files
committed
ManagerRegistry::getRepository returns correct EntityRepository
1 parent 77a6f9e commit 2910fcc

8 files changed

+160
-41
lines changed

extension.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@ services:
3232
class: PHPStan\Type\Doctrine\ObjectRepositoryDynamicReturnTypeExtension
3333
tags:
3434
- phpstan.broker.dynamicMethodReturnTypeExtension
35+
-
36+
class: PHPStan\Type\Doctrine\ManagerRegistryGetRepositoryDynamicReturnTypeExtension
37+
arguments:
38+
repositoryClass: %doctrine.repositoryClass%
39+
tags:
40+
- phpstan.broker.dynamicMethodReturnTypeExtension
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Doctrine;
4+
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\MethodReflection;
8+
use PHPStan\Reflection\ParametersAcceptorSelector;
9+
use PHPStan\Type\Constant\ConstantStringType;
10+
use PHPStan\Type\MixedType;
11+
use PHPStan\Type\Type;
12+
13+
abstract class GetRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension
14+
{
15+
16+
/** @var string */
17+
private $repositoryClass;
18+
19+
public function __construct(string $repositoryClass)
20+
{
21+
$this->repositoryClass = $repositoryClass;
22+
}
23+
24+
public function isMethodSupported(MethodReflection $methodReflection): bool
25+
{
26+
return $methodReflection->getName() === 'getRepository';
27+
}
28+
29+
public function getTypeFromMethodCall(
30+
MethodReflection $methodReflection,
31+
MethodCall $methodCall,
32+
Scope $scope
33+
): Type
34+
{
35+
if (count($methodCall->args) === 0) {
36+
return ParametersAcceptorSelector::selectSingle(
37+
$methodReflection->getVariants()
38+
)->getReturnType();
39+
}
40+
$argType = $scope->getType($methodCall->args[0]->value);
41+
if (!$argType instanceof ConstantStringType) {
42+
return new MixedType();
43+
}
44+
45+
return new ObjectRepositoryType($argType->getValue(), $this->repositoryClass);
46+
}
47+
48+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Doctrine;
4+
5+
class ManagerRegistryGetRepositoryDynamicReturnTypeExtension extends GetRepositoryDynamicReturnTypeExtension
6+
{
7+
8+
public function getClass(): string
9+
{
10+
return 'Doctrine\Common\Persistence\ManagerRegistry';
11+
}
12+
13+
}

src/Type/Doctrine/ObjectManagerGetRepositoryDynamicReturnTypeExtension.php

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,12 @@
22

33
namespace PHPStan\Type\Doctrine;
44

5-
use PhpParser\Node\Expr\MethodCall;
6-
use PHPStan\Analyser\Scope;
7-
use PHPStan\Reflection\MethodReflection;
8-
use PHPStan\Reflection\ParametersAcceptorSelector;
9-
use PHPStan\Type\Constant\ConstantStringType;
10-
use PHPStan\Type\MixedType;
11-
use PHPStan\Type\Type;
12-
13-
class ObjectManagerGetRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension
5+
class ObjectManagerGetRepositoryDynamicReturnTypeExtension extends GetRepositoryDynamicReturnTypeExtension
146
{
157

16-
/** @var string */
17-
private $repositoryClass;
18-
19-
public function __construct(string $repositoryClass)
20-
{
21-
$this->repositoryClass = $repositoryClass;
22-
}
23-
248
public function getClass(): string
259
{
2610
return 'Doctrine\Common\Persistence\ObjectManager';
2711
}
2812

29-
public function isMethodSupported(MethodReflection $methodReflection): bool
30-
{
31-
return $methodReflection->getName() === 'getRepository';
32-
}
33-
34-
public function getTypeFromMethodCall(
35-
MethodReflection $methodReflection,
36-
MethodCall $methodCall,
37-
Scope $scope
38-
): Type
39-
{
40-
if (count($methodCall->args) === 0) {
41-
return ParametersAcceptorSelector::selectSingle(
42-
$methodReflection->getVariants()
43-
)->getReturnType();
44-
}
45-
$argType = $scope->getType($methodCall->args[0]->value);
46-
if (!$argType instanceof ConstantStringType) {
47-
return new MixedType();
48-
}
49-
50-
return new ObjectRepositoryType($argType->getValue(), $this->repositoryClass);
51-
}
52-
5313
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DoctrineIntegration\Persistence;
4+
5+
use PHPStan\Testing\LevelsTestCase;
6+
7+
final class ManagerRegistryIntegrationTest extends LevelsTestCase
8+
{
9+
10+
/**
11+
* @return string[][]
12+
*/
13+
public function dataTopics(): array
14+
{
15+
return [
16+
['managerRegistryRepositoryDynamicReturn'],
17+
];
18+
}
19+
20+
public function getDataPath(): string
21+
{
22+
return __DIR__ . '/data';
23+
}
24+
25+
public function getPhpStanExecutablePath(): string
26+
{
27+
return __DIR__ . '/../../../vendor/bin/phpstan';
28+
}
29+
30+
public function getPhpStanConfigPath(): ?string
31+
{
32+
return __DIR__ . '/phpstan.neon';
33+
}
34+
35+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"message": "Call to an undefined method Doctrine\\ORM\\EntityRepository<PHPStan\\DoctrineIntegration\\Persistence\\ManagerRegistryRepositoryDynamicReturn\\MyEntity>::nonexistant().",
4+
"line": 31,
5+
"ignorable": true
6+
}
7+
]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DoctrineIntegration\Persistence\ManagerRegistryRepositoryDynamicReturn;
4+
5+
use Doctrine\Common\Persistence\ManagerRegistry;
6+
use Doctrine\ORM\EntityManagerInterface;
7+
use Doctrine\ORM\Mapping as ORM;
8+
use RuntimeException;
9+
10+
class Example
11+
{
12+
/**
13+
* @var ManagerRegistry
14+
*/
15+
private $managerRegistry;
16+
17+
public function __construct(ManagerRegistry $managerRegistry)
18+
{
19+
$this->managerRegistry = $managerRegistry;
20+
}
21+
22+
public function findDynamicType(): void
23+
{
24+
$test = $this->managerRegistry->getRepository(MyEntity::class)->createQueryBuilder('e');
25+
26+
$test->getQuery();
27+
}
28+
29+
public function errorWithDynamicType(): void
30+
{
31+
$this->managerRegistry->getRepository(MyEntity::class)->nonexistant();
32+
}
33+
}
34+
35+
/**
36+
* @ORM\Entity()
37+
*/
38+
class MyEntity
39+
{
40+
/**
41+
* @ORM\Id()
42+
* @ORM\GeneratedValue()
43+
* @ORM\Column(type="integer")
44+
*
45+
* @var int
46+
*/
47+
private $id;
48+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
includes:
2+
- ../../../extension.neon

0 commit comments

Comments
 (0)