4
4
5
5
namespace Rector \FamilyTree \NodeAnalyzer ;
6
6
7
+ use PhpParser \Node ;
8
+ use PhpParser \Node \Stmt \ClassLike ;
9
+ use PhpParser \Node \Stmt \ClassMethod ;
7
10
use PHPStan \Reflection \ClassReflection ;
8
11
use PHPStan \Reflection \ParametersAcceptorSelector ;
9
12
use PHPStan \Reflection \Php \PhpMethodReflection ;
10
13
use PHPStan \Type \MixedType ;
14
+ use PHPStan \Type \StringType ;
11
15
use PHPStan \Type \Type ;
16
+ use Rector \PhpParser \AstResolver ;
12
17
13
18
final readonly class ClassChildAnalyzer
14
19
{
20
+ public function __construct (
21
+ private AstResolver $ astResolver
22
+ ) {
23
+ }
24
+
15
25
/**
16
26
* Look both parent class and interface, yes, all PHP interface methods are abstract
17
27
*/
@@ -42,8 +52,14 @@ public function resolveParentClassMethodReturnType(ClassReflection $classReflect
42
52
}
43
53
44
54
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
+ }
47
63
48
64
if (! $ nativeReturnType instanceof MixedType) {
49
65
return $ nativeReturnType ;
@@ -53,6 +69,27 @@ public function resolveParentClassMethodReturnType(ClassReflection $classReflect
53
69
return new MixedType ();
54
70
}
55
71
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
+
56
93
/**
57
94
* @return PhpMethodReflection[]
58
95
*/
0 commit comments