22
33namespace PHPStan \Type \Doctrine ;
44
5+ use Doctrine \ODM \MongoDB \DocumentManager ;
6+ use Doctrine \ODM \MongoDB \Repository \DocumentRepository ;
7+ use Doctrine \ORM \EntityRepository ;
58use PhpParser \Node \Expr \MethodCall ;
69use PHPStan \Analyser \Scope ;
710use PHPStan \Reflection \MethodReflection ;
811use PHPStan \Reflection \ParametersAcceptorSelector ;
12+ use PHPStan \Reflection \ReflectionProvider ;
913use PHPStan \Type \Constant \ConstantStringType ;
1014use PHPStan \Type \Generic \GenericClassStringType ;
1115use PHPStan \Type \Generic \GenericObjectType ;
1721class GetRepositoryDynamicReturnTypeExtension implements \PHPStan \Type \DynamicMethodReturnTypeExtension
1822{
1923
24+ /** @var ReflectionProvider */
25+ private $ reflectionProvider ;
26+
27+ /** @var string|null */
28+ private $ repositoryClass ;
29+
30+ /** @var string|null */
31+ private $ ormRepositoryClass ;
32+
33+ /** @var string|null */
34+ private $ odmRepositoryClass ;
35+
2036 /** @var string */
2137 private $ managerClass ;
2238
2339 /** @var ObjectMetadataResolver */
2440 private $ metadataResolver ;
2541
2642 public function __construct (
43+ ReflectionProvider $ reflectionProvider ,
44+ ?string $ repositoryClass ,
45+ ?string $ ormRepositoryClass ,
46+ ?string $ odmRepositoryClass ,
2747 string $ managerClass ,
2848 ObjectMetadataResolver $ metadataResolver
2949 )
3050 {
51+ $ this ->reflectionProvider = $ reflectionProvider ;
52+ $ this ->repositoryClass = $ repositoryClass ;
53+ $ this ->ormRepositoryClass = $ ormRepositoryClass ;
54+ $ this ->odmRepositoryClass = $ odmRepositoryClass ;
3155 $ this ->managerClass = $ managerClass ;
3256 $ this ->metadataResolver = $ metadataResolver ;
3357 }
@@ -48,9 +72,15 @@ public function getTypeFromMethodCall(
4872 Scope $ scope
4973 ): Type
5074 {
75+ $ calledOnType = $ scope ->getType ($ methodCall ->var );
76+ if ((new ObjectType (DocumentManager::class))->isSuperTypeOf ($ calledOnType )->yes ()) {
77+ $ defaultRepositoryClass = $ this ->odmRepositoryClass ?? $ this ->repositoryClass ?? DocumentRepository::class;
78+ } else {
79+ $ defaultRepositoryClass = $ this ->ormRepositoryClass ?? $ this ->repositoryClass ?? EntityRepository::class;
80+ }
5181 if (count ($ methodCall ->getArgs ()) === 0 ) {
5282 return new GenericObjectType (
53- $ this -> metadataResolver -> getResolvedRepositoryClass () ,
83+ $ defaultRepositoryClass ,
5484 [new ObjectWithoutClassType ()]
5585 );
5686 }
@@ -62,20 +92,20 @@ public function getTypeFromMethodCall(
6292 $ classType = $ argType ->getGenericType ();
6393 if (!$ classType instanceof TypeWithClassName) {
6494 return new GenericObjectType (
65- $ this -> metadataResolver -> getResolvedRepositoryClass () ,
95+ $ defaultRepositoryClass ,
6696 [$ classType ]
6797 );
6898 }
6999
70100 $ objectName = $ classType ->getClassName ();
71101 } else {
72- return $ this ->getDefaultReturnType ($ scope , $ methodCall ->getArgs (), $ methodReflection );
102+ return $ this ->getDefaultReturnType ($ scope , $ methodCall ->getArgs (), $ methodReflection, $ defaultRepositoryClass );
73103 }
74104
75105 try {
76- $ repositoryClass = $ this ->metadataResolver -> getRepositoryClass ($ objectName );
106+ $ repositoryClass = $ this ->getRepositoryClass ($ objectName, $ defaultRepositoryClass );
77107 } catch (\Doctrine \ORM \Mapping \MappingException $ e ) {
78- return $ this ->getDefaultReturnType ($ scope , $ methodCall ->getArgs (), $ methodReflection );
108+ return $ this ->getDefaultReturnType ($ scope , $ methodCall ->getArgs (), $ methodReflection, $ defaultRepositoryClass );
79109 }
80110
81111 return new GenericObjectType ($ repositoryClass , [
@@ -89,7 +119,7 @@ public function getTypeFromMethodCall(
89119 * @param \PHPStan\Reflection\MethodReflection $methodReflection
90120 * @return \PHPStan\Type\Type
91121 */
92- private function getDefaultReturnType (Scope $ scope , array $ args , MethodReflection $ methodReflection ): Type
122+ private function getDefaultReturnType (Scope $ scope , array $ args , MethodReflection $ methodReflection, string $ defaultRepositoryClass ): Type
93123 {
94124 $ defaultType = ParametersAcceptorSelector::selectFromArgs (
95125 $ scope ,
@@ -98,12 +128,44 @@ private function getDefaultReturnType(Scope $scope, array $args, MethodReflectio
98128 )->getReturnType ();
99129 if ($ defaultType instanceof GenericObjectType && count ($ defaultType ->getTypes ()) > 0 ) {
100130 return new GenericObjectType (
101- $ this -> metadataResolver -> getResolvedRepositoryClass () ,
131+ $ defaultRepositoryClass ,
102132 [$ defaultType ->getTypes ()[0 ]]
103133 );
104134 }
105135
106136 return $ defaultType ;
107137 }
108138
139+ private function getRepositoryClass (string $ className , string $ defaultRepositoryClass ): string
140+ {
141+ if (!$ this ->reflectionProvider ->hasClass ($ className )) {
142+ return $ defaultRepositoryClass ;
143+ }
144+
145+ $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
146+ if ($ classReflection ->isInterface () || $ classReflection ->isTrait ()) {
147+ return $ defaultRepositoryClass ;
148+ }
149+
150+ $ metadata = $ this ->metadataResolver ->getClassMetadata ($ classReflection ->getName ());
151+ if ($ metadata !== null ) {
152+ return $ metadata ->customRepositoryClassName ?? $ defaultRepositoryClass ;
153+ }
154+
155+ $ objectManager = $ this ->metadataResolver ->getObjectManager ();
156+ if ($ objectManager === null ) {
157+ return $ defaultRepositoryClass ;
158+ }
159+
160+ $ metadata = $ objectManager ->getClassMetadata ($ classReflection ->getName ());
161+ $ odmMetadataClass = 'Doctrine\ODM\MongoDB\Mapping\ClassMetadata ' ;
162+ if ($ metadata instanceof $ odmMetadataClass ) {
163+ /** @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata<object> $odmMetadata */
164+ $ odmMetadata = $ metadata ;
165+ return $ odmMetadata ->customRepositoryClassName ?? $ defaultRepositoryClass ;
166+ }
167+
168+ return $ defaultRepositoryClass ;
169+ }
170+
109171}
0 commit comments