@@ -75,74 +75,6 @@ public function getClass(): string
7575 return ActiveRecord::class;
7676 }
7777
78- /**
79- * Determines whether the specified static method is supported for dynamic return type inference.
80- *
81- * Inspects the method's return type and class context to decide if the static method should be handled by this
82- * extension for dynamic return type analysis.
83- *
84- * This includes methods returning `$this`, union types containing {@see ActiveRecord} subclasses, or types related
85- * to {@see ActiveQuery}.
86- *
87- * This method enables PHPStan to apply custom type inference for static {@see ActiveRecord} query methods ensuring
88- * accurate autocompletion and static analysis for methods such as {@see ActiveRecord::findOne()},
89- * {@see ActiveRecord::findAll()}, and custom query builders.
90- *
91- * @param MethodReflection $methodReflection Reflection instance for the method being analyzed.
92- *
93- * @return bool `true` if the static method is supported for dynamic return type inference; `false` otherwise.
94- */
95- public function isStaticMethodSupported (MethodReflection $ methodReflection ): bool
96- {
97- $ variants = $ methodReflection ->getVariants ();
98-
99- if (count ($ variants ) === 0 ) {
100- return false ;
101- }
102-
103- $ returnType = $ variants [0 ]->getReturnType ();
104-
105- if ($ returnType ::class === ThisType::class) {
106- return true ;
107- }
108-
109- if ($ returnType ::class === UnionType::class) {
110- foreach ($ returnType ->getTypes () as $ type ) {
111- $ classNames = $ type ->getObjectClassNames ();
112-
113- if (count ($ classNames ) > 0 ) {
114- $ className = $ classNames [0 ];
115-
116- if ($ this ->reflectionProvider ->hasClass ($ className )) {
117- $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
118-
119- return $ classReflection ->isSubclassOfClass (
120- $ this ->reflectionProvider ->getClass ($ this ->getClass ()),
121- );
122- }
123- }
124- }
125- }
126-
127- $ classNames = $ returnType ->getObjectClassNames ();
128-
129- if (count ($ classNames ) > 0 ) {
130- $ className = $ classNames [0 ];
131-
132- if ($ className === ActiveQuery::class) {
133- return true ;
134- }
135-
136- if ($ this ->reflectionProvider ->hasClass ($ className )) {
137- $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
138-
139- return $ classReflection ->isSubclassOfClass ($ this ->reflectionProvider ->getClass (ActiveQuery::class));
140- }
141- }
142-
143- return $ returnType ::class === GenericObjectType::class && $ returnType ->getClassName () === ActiveQuery::class;
144- }
145-
14678 /**
14779 * Infers the return type for a static method call on an {@see ActiveRecord} class based on method signature and
14880 * context.
@@ -225,30 +157,71 @@ public function getTypeFromStaticMethodCall(
225157 }
226158
227159 /**
228- * Determines whether the specified class is {@see ActiveRecord} or a subclass .
160+ * Determines whether the specified static method is supported for dynamic return type inference .
229161 *
230- * Checks if the given class name corresponds to the {@see ActiveRecord} base class or any of its subclasses by
231- * leveraging the reflection provider .
162+ * Inspects the method's return type and class context to decide if the static method should be handled by this
163+ * extension for dynamic return type analysis .
232164 *
233- * This is used to ensure type compatibility and accurate type inference for static {@see ActiveRecord} methods
234- * during PHPStan analysis .
165+ * This includes methods returning `$this`, union types containing {@see ActiveRecord} subclasses, or types related
166+ * to {@see ActiveQuery} .
235167 *
236- * This method is essential for supporting dynamic return type inference in scenarios where union types or generic
237- * {@see ActiveRecord} subclasses are involved, enabling precise type checks and autocompletion.
168+ * This method enables PHPStan to apply custom type inference for static {@see ActiveRecord} query methods ensuring
169+ * accurate autocompletion and static analysis for methods such as {@see ActiveRecord::findOne()},
170+ * {@see ActiveRecord::findAll()}, and custom query builders.
238171 *
239- * @param string $className Fully qualified class name to check .
172+ * @param MethodReflection $methodReflection Reflection instance for the method being analyzed .
240173 *
241- * @return bool `true` if the class is {@see ActiveRecord} or a subclass ; `false` otherwise.
174+ * @return bool `true` if the static method is supported for dynamic return type inference ; `false` otherwise.
242175 */
243- private function isActiveRecordClass ( string $ className ): bool
176+ public function isStaticMethodSupported ( MethodReflection $ methodReflection ): bool
244177 {
245- if ($ this ->reflectionProvider ->hasClass ($ className ) === false ) {
178+ $ variants = $ methodReflection ->getVariants ();
179+
180+ if (count ($ variants ) === 0 ) {
246181 return false ;
247182 }
248183
249- return $ this ->reflectionProvider ->getClass ($ className )->isSubclassOfClass (
250- $ this ->reflectionProvider ->getClass (ActiveRecord::class),
251- );
184+ $ returnType = $ variants [0 ]->getReturnType ();
185+
186+ if ($ returnType ::class === ThisType::class) {
187+ return true ;
188+ }
189+
190+ if ($ returnType ::class === UnionType::class) {
191+ foreach ($ returnType ->getTypes () as $ type ) {
192+ $ classNames = $ type ->getObjectClassNames ();
193+
194+ if (count ($ classNames ) > 0 ) {
195+ $ className = $ classNames [0 ];
196+
197+ if ($ this ->reflectionProvider ->hasClass ($ className )) {
198+ $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
199+
200+ return $ classReflection ->isSubclassOfClass (
201+ $ this ->reflectionProvider ->getClass ($ this ->getClass ()),
202+ );
203+ }
204+ }
205+ }
206+ }
207+
208+ $ classNames = $ returnType ->getObjectClassNames ();
209+
210+ if (count ($ classNames ) > 0 ) {
211+ $ className = $ classNames [0 ];
212+
213+ if ($ className === ActiveQuery::class) {
214+ return true ;
215+ }
216+
217+ if ($ this ->reflectionProvider ->hasClass ($ className )) {
218+ $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
219+
220+ return $ classReflection ->isSubclassOfClass ($ this ->reflectionProvider ->getClass (ActiveQuery::class));
221+ }
222+ }
223+
224+ return $ returnType ::class === GenericObjectType::class && $ returnType ->getClassName () === ActiveQuery::class;
252225 }
253226
254227 /**
@@ -277,4 +250,31 @@ private function isActiveQueryClass(string $className): bool
277250 $ this ->reflectionProvider ->getClass (ActiveQuery::class),
278251 );
279252 }
253+
254+ /**
255+ * Determines whether the specified class is {@see ActiveRecord} or a subclass.
256+ *
257+ * Checks if the given class name corresponds to the {@see ActiveRecord} base class or any of its subclasses by
258+ * leveraging the reflection provider.
259+ *
260+ * This is used to ensure type compatibility and accurate type inference for static {@see ActiveRecord} methods
261+ * during PHPStan analysis.
262+ *
263+ * This method is essential for supporting dynamic return type inference in scenarios where union types or generic
264+ * {@see ActiveRecord} subclasses are involved, enabling precise type checks and autocompletion.
265+ *
266+ * @param string $className Fully qualified class name to check.
267+ *
268+ * @return bool `true` if the class is {@see ActiveRecord} or a subclass; `false` otherwise.
269+ */
270+ private function isActiveRecordClass (string $ className ): bool
271+ {
272+ if ($ this ->reflectionProvider ->hasClass ($ className ) === false ) {
273+ return false ;
274+ }
275+
276+ return $ this ->reflectionProvider ->getClass ($ className )->isSubclassOfClass (
277+ $ this ->reflectionProvider ->getClass (ActiveRecord::class),
278+ );
279+ }
280280}
0 commit comments