44
55namespace Rector \FamilyTree \NodeAnalyzer ;
66
7+ use PhpParser \Node ;
8+ use PhpParser \Node \Stmt \ClassLike ;
9+ use PhpParser \Node \Stmt \ClassMethod ;
710use PHPStan \Reflection \ClassReflection ;
811use PHPStan \Reflection \ParametersAcceptorSelector ;
912use PHPStan \Reflection \Php \PhpMethodReflection ;
1013use PHPStan \Type \MixedType ;
14+ use PHPStan \Type \StringType ;
1115use PHPStan \Type \Type ;
16+ use Rector \PhpParser \AstResolver ;
1217
1318final readonly class ClassChildAnalyzer
1419{
20+ public function __construct (
21+ private AstResolver $ astResolver
22+ ) {
23+ }
24+
1525 /**
1626 * Look both parent class and interface, yes, all PHP interface methods are abstract
1727 */
@@ -42,8 +52,14 @@ public function resolveParentClassMethodReturnType(ClassReflection $classReflect
4252 }
4353
4454 foreach ($ parentClassMethods as $ parentClassMethod ) {
45- $ parametersAcceptor = ParametersAcceptorSelector::combineAcceptors ($ parentClassMethod ->getVariants ());
46- $ nativeReturnType = $ parametersAcceptor ->getNativeReturnType ();
55+ // for downgrade purpose on __toString
56+ // @see https://3v4l.org/kdcEh#v7.4.33
57+ // @see https://github.com/phpstan/phpstan-src/commit/3854cbc5748a7cb51ee0b86ceffe29bd0564bc98
58+ if ($ parentClassMethod ->getDeclaringClass ()->isBuiltIn () || $ methodName !== '__toString ' ) {
59+ $ nativeReturnType = $ this ->resolveNativeType ($ parentClassMethod );
60+ } else {
61+ $ nativeReturnType = $ this ->resolveToStringNativeTypeFromAstResolver ($ parentClassMethod );
62+ }
4763
4864 if (! $ nativeReturnType instanceof MixedType) {
4965 return $ nativeReturnType ;
@@ -53,6 +69,27 @@ public function resolveParentClassMethodReturnType(ClassReflection $classReflect
5369 return new MixedType ();
5470 }
5571
72+ private function resolveNativeType (PhpMethodReflection $ phpMethodReflection ): Type
73+ {
74+ $ extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors ($ phpMethodReflection ->getVariants ());
75+ return $ extendedParametersAcceptor ->getNativeReturnType ();
76+ }
77+
78+ private function resolveToStringNativeTypeFromAstResolver (PhpMethodReflection $ phpMethodReflection ): Type
79+ {
80+ $ classReflection = $ phpMethodReflection ->getDeclaringClass ();
81+ $ class = $ this ->astResolver ->resolveClassFromClassReflection ($ classReflection );
82+
83+ if ($ class instanceof ClassLike) {
84+ $ classMethod = $ class ->getMethod ($ phpMethodReflection ->getName ());
85+ if ($ classMethod instanceof ClassMethod && !$ classMethod ->returnType instanceof Node) {
86+ return new MixedType ();
87+ }
88+ }
89+
90+ return new StringType ();
91+ }
92+
5693 /**
5794 * @return PhpMethodReflection[]
5895 */
0 commit comments