Skip to content

Commit 64e1ecc

Browse files
committed
Fixed
1 parent 496aee1 commit 64e1ecc

File tree

5 files changed

+143
-92
lines changed

5 files changed

+143
-92
lines changed

src/DoctrineClassMetadataProvider.php

Lines changed: 66 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan;
44

55
use Doctrine\Common\Annotations\AnnotationReader;
6+
use Doctrine\Common\Annotations\AnnotationRegistry;
67
use Doctrine\Common\EventManager;
78
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
89
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
@@ -14,68 +15,74 @@
1415
class DoctrineClassMetadataProvider
1516
{
1617

17-
/**
18-
* @var EntityManager
19-
*/
20-
private $em;
18+
/** @var EntityManager */
19+
private $em;
2120

22-
/**
23-
* @var string
24-
*/
25-
private $repositoryClass;
21+
/** @var string */
22+
private $repositoryClass;
2623

27-
public function __construct(string $repositoryClass, array $mapping)
28-
{
29-
$configuration = new Configuration();
30-
$configuration->setDefaultRepositoryClassName($repositoryClass);
31-
$configuration->setMetadataDriverImpl($this->setupMappingDriver($mapping));
32-
$configuration->setProxyDir('/dev/null');
33-
$configuration->setProxyNamespace('__DP__');
34-
$configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);
35-
$evm = new EventManager();
36-
$this->em = EntityManager::create(
37-
\Doctrine\DBAL\DriverManager::getConnection(['host' => '/:memory:', 'driver' => 'pdo_sqlite'], $configuration, $evm),
38-
$configuration,
39-
$evm
40-
);
41-
$this->repositoryClass = $repositoryClass;
42-
}
24+
/**
25+
* @param string $repositoryClass
26+
* @param mixed[] $mapping
27+
*/
28+
public function __construct(string $repositoryClass, array $mapping)
29+
{
30+
AnnotationRegistry::registerLoader('class_exists');
31+
$configuration = new Configuration();
32+
$configuration->setDefaultRepositoryClassName($repositoryClass);
33+
$configuration->setMetadataDriverImpl($this->setupMappingDriver($mapping));
34+
$configuration->setProxyDir('/dev/null');
35+
$configuration->setProxyNamespace('__DP__');
36+
$configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);
37+
$evm = new EventManager();
38+
$this->em = EntityManager::create(
39+
\Doctrine\DBAL\DriverManager::getConnection(['host' => '/:memory:', 'driver' => 'pdo_sqlite'], $configuration, $evm),
40+
$configuration,
41+
$evm
42+
);
43+
$this->repositoryClass = $repositoryClass;
44+
}
4345

44-
private function setupMappingDriver(array $mapping): MappingDriver
45-
{
46-
$driver = new MappingDriverChain();
47-
foreach ($mapping as $namespace => $config) {
48-
switch ($config['type']) {
49-
case 'annotation':
50-
$nested = new Mapping\Driver\AnnotationDriver(new AnnotationReader(), $config['paths']);
51-
break;
52-
case 'yml':
53-
case 'yaml':
54-
$nested = new Mapping\Driver\YamlDriver($config['paths']);
55-
break;
56-
case 'xml':
57-
$nested = new Mapping\Driver\XmlDriver($config['paths']);
58-
break;
59-
default:
60-
throw new \InvalidArgumentException('Unknown mapping type: ' . $config['type']);
61-
}
62-
$driver->addDriver($nested, $namespace);
63-
}
64-
return $driver;
65-
}
46+
/**
47+
* @param mixed[] $mapping
48+
*/
49+
private function setupMappingDriver(array $mapping): MappingDriver
50+
{
51+
$driver = new MappingDriverChain();
52+
foreach ($mapping as $namespace => $config) {
53+
switch ($config['type']) {
54+
case 'annotation':
55+
$nested = new Mapping\Driver\AnnotationDriver(new AnnotationReader(), $config['paths']);
56+
break;
57+
case 'yml':
58+
case 'yaml':
59+
$nested = new Mapping\Driver\YamlDriver($config['paths']);
60+
break;
61+
case 'xml':
62+
$nested = new Mapping\Driver\XmlDriver($config['paths']);
63+
break;
64+
default:
65+
throw new \InvalidArgumentException('Unknown mapping type: ' . $config['type']);
66+
}
67+
$driver->addDriver($nested, $namespace);
68+
}
69+
return $driver;
70+
}
6671

67-
public function getBaseRepositoryClass(): string
68-
{
69-
return $this->repositoryClass;
70-
}
72+
public function getBaseRepositoryClass(): string
73+
{
74+
return $this->repositoryClass;
75+
}
76+
77+
/**
78+
* @param string $className
79+
* @return Mapping\ClassMetadataInfo
80+
* @throws \Doctrine\Common\Persistence\Mapping\MappingException
81+
* @throws \ReflectionException
82+
*/
83+
public function getMetadataFor(string $className): Mapping\ClassMetadataInfo
84+
{
85+
return $this->em->getClassMetadata($className);
86+
}
7187

72-
/**
73-
* @throws \Doctrine\Common\Persistence\Mapping\MappingException
74-
* @throws \ReflectionException
75-
*/
76-
public function getMetadataFor(string $className): Mapping\ClassMetadataInfo
77-
{
78-
return $this->em->getClassMetadata($className);
79-
}
80-
8188
}

src/Type/Doctrine/ObjectManagerGetRepositoryDynamicReturnTypeExtension.php

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,13 @@
1515

1616
class ObjectManagerGetRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension
1717
{
18+
/** @var DoctrineClassMetadataProvider */
19+
private $metadataProvider;
1820

19-
/** @var string */
20-
private $repositoryClass;
21-
22-
/**
23-
* @var DoctrineClassMetadataProvider
24-
*/
25-
private $metadataProvider;
26-
27-
public function __construct(DoctrineClassMetadataProvider $metadataProvider)
21+
public function __construct(DoctrineClassMetadataProvider $metadataProvider)
2822
{
29-
$this->metadataProvider = $metadataProvider;
30-
AnnotationRegistry::registerLoader('class_exists');
31-
}
23+
$this->metadataProvider = $metadataProvider;
24+
}
3225

3326
public function getClass(): string
3427
{
@@ -56,28 +49,12 @@ public function getTypeFromMethodCall(
5649
return new MixedType();
5750
}
5851

59-
return new ObjectRepositoryType($argType->getValue(), $this->repositoryClass);
60-
61-
$class = $arg->class;
62-
if (!($class instanceof \PhpParser\Node\Name)) {
63-
return $methodReflection->getReturnType();
52+
try {
53+
$metadata = $this->metadataProvider->getMetadataFor($argType->getValue());
54+
return new ObjectRepositoryType($argType->getValue(), $metadata->customRepositoryClassName ?: $this->metadataProvider->getBaseRepositoryClass());
55+
} catch (MappingException $e) {
56+
return new ObjectRepositoryType($argType->getValue(), $this->metadataProvider->getBaseRepositoryClass());
6457
}
65-
66-
$class = (string) $class;
67-
if ($class === 'static') {
68-
return $methodReflection->getReturnType();
69-
}
70-
71-
if ($class === 'self') {
72-
$class = $scope->getClassReflection()->getName();
73-
}
74-
75-
try {
76-
$metadata = $this->metadataProvider->getMetadataFor($class);
77-
return new EntityRepositoryType($class, $metadata->customRepositoryClassName ?: $this->metadataProvider->getBaseRepositoryClass());
78-
} catch (MappingException $e) {
79-
return new EntityRepositoryType($class, $this->metadataProvider->getBaseRepositoryClass());
80-
}
8158
}
8259

8360
}

tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function dataTopics(): array
1515
return [
1616
['entityManagerDynamicReturn'],
1717
['entityRepositoryDynamicReturn'],
18+
['entityManagerDynamicReturnRepository'],
1819
['entityManagerMergeReturn'],
1920
];
2021
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DoctrineIntegration\ORM\EntityManagerDynamicReturnRepository;
4+
5+
use Doctrine\ORM\EntityManagerInterface;
6+
use Doctrine\ORM\EntityRepository;
7+
use Doctrine\ORM\Mapping as ORM;
8+
use RuntimeException;
9+
10+
class Example
11+
{
12+
/**
13+
* @var EntityManagerInterface
14+
*/
15+
private $entityManager;
16+
17+
public function __construct(EntityManagerInterface $entityManager)
18+
{
19+
$this->entityManager = $entityManager;
20+
}
21+
22+
public function findDynamicType(): void
23+
{
24+
$test = $this->entityManager->getRepository(MyEntity::class)->findMyEntity(1);
25+
26+
if ($test === null) {
27+
throw new RuntimeException('Sorry, but no...');
28+
}
29+
30+
$test->doSomething();
31+
}
32+
}
33+
34+
/**
35+
* @ORM\Entity(repositoryClass="MyEntityRepository")
36+
*/
37+
class MyEntity
38+
{
39+
/**
40+
* @ORM\Id()
41+
* @ORM\GeneratedValue()
42+
* @ORM\Column(type="integer")
43+
*
44+
* @var int
45+
*/
46+
private $id;
47+
48+
public function doSomething(): void
49+
{
50+
}
51+
}
52+
53+
class MyEntityRepository extends EntityRepository
54+
{
55+
public function findMyEntity($id): ?MyEntity
56+
{
57+
return $this->findOneBy([
58+
'id' => $id
59+
]);
60+
}
61+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
includes:
22
- ../../../extension.neon
3+
parameters:
4+
doctrine:
5+
metadata:
6+
PHPStan\DoctrineIntegration\ORM\EntityManagerDynamicReturnRepository:
7+
type: annotation

0 commit comments

Comments
 (0)