1717use PHPStan \Type \IntegerType ;
1818use PHPStan \Type \IntersectionType ;
1919use PHPStan \Type \IterableType ;
20+ use PHPStan \Type \MixedType ;
2021use PHPStan \Type \ObjectType ;
2122use PHPStan \Type \Type ;
2223use PHPStan \Type \TypeCombinator ;
24+ use PHPStan \Type \TypeTraverser ;
2325use PHPStan \Type \TypeWithClassName ;
26+ use PHPStan \Type \UnionType ;
2427
2528
2629class RepositoryReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -32,7 +35,10 @@ class RepositoryReturnTypeExtension implements DynamicMethodReturnTypeExtension
3235 private $ reflectionProvider ;
3336
3437
35- public function __construct (RepositoryEntityTypeHelper $ repositoryEntityTypeHelper , ReflectionProvider $ reflectionProvider )
38+ public function __construct (
39+ RepositoryEntityTypeHelper $ repositoryEntityTypeHelper ,
40+ ReflectionProvider $ reflectionProvider
41+ )
3642 {
3743 $ this ->repositoryEntityTypeHelper = $ repositoryEntityTypeHelper ;
3844 $ this ->reflectionProvider = $ reflectionProvider ;
@@ -71,15 +77,38 @@ public function getTypeFromMethodCall(
7177 ): Type
7278 {
7379 $ repository = $ scope ->getType ($ methodCall ->var );
74- \assert ($ repository instanceof TypeWithClassName);
80+ $ repositoryType = new ObjectType (IRepository::class);
81+ return TypeTraverser::map (
82+ $ repository ,
83+ function ($ type , $ traverse ) use ($ repositoryType , $ methodReflection , $ scope ): Type {
84+ if ($ type instanceof UnionType || $ type instanceof IntersectionType) {
85+ return $ traverse ($ type );
86+ }
87+ if (!$ type instanceof TypeWithClassName || $ repositoryType ->isSuperTypeOf ($ type )->no ()) {
88+ return new MixedType ();
89+ }
90+
91+ /** @var class-string<Repository> $repositoryClassName */
92+ $ repositoryClassName = $ type ->getClassName ();
93+ return $ this ->getEntityTypeFromMethodClass ($ repositoryClassName , $ methodReflection , $ scope );
94+ }
95+ );
96+ }
7597
76- if ($ repository ->getClassName () === Repository::class) {
98+
99+ /**
100+ * @param class-string<Repository> $repositoryClassName
101+ */
102+ private function getEntityTypeFromMethodClass (
103+ string $ repositoryClassName ,
104+ MethodReflection $ methodReflection ,
105+ Scope $ scope
106+ ): Type
107+ {
108+ if ($ repositoryClassName === Repository::class) {
77109 return ParametersAcceptorSelector::selectSingle ($ methodReflection ->getVariants ())->getReturnType ();
78110 }
79111
80- /** @phpstan-var class-string<Repository> $repositoryClassName */
81- $ repositoryClassName = $ repository ->getClassName ();
82-
83112 try {
84113 $ repositoryReflection = $ this ->reflectionProvider ->getClass ($ repositoryClassName );
85114 } catch (ClassNotFoundException $ e ) {
0 commit comments