4
4
5
5
namespace FriendsOfTYPO3 \PHPStan \TYPO3 \Type ;
6
6
7
- use PhpParser \Node \Arg ;
8
- use PhpParser \Node \Expr ;
7
+ use PhpParser \Node \Arg as ArgumentNode ;
9
8
use PhpParser \Node \Expr \ClassConstFetch ;
10
9
use PhpParser \Node \Expr \StaticCall ;
11
10
use PhpParser \Node \Name ;
11
+ use PhpParser \Node \Scalar \String_ as StringNode ;
12
12
use PHPStan \Analyser \Scope ;
13
+ use PHPStan \Reflection \ClassReflection ;
13
14
use PHPStan \Reflection \MethodReflection ;
14
15
use PHPStan \Type \DynamicStaticMethodReturnTypeExtension ;
15
16
use PHPStan \Type \ObjectType ;
16
17
use PHPStan \Type \ObjectWithoutClassType ;
17
- use PHPStan \Type \Type ;
18
+ use PHPStan \Type \StaticType ;
19
+ use PHPStan \Type \Type as TypeInterface ;
18
20
use TYPO3 \CMS \Core \Utility \GeneralUtility ;
19
21
20
22
class GeneralUtilityDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
@@ -29,32 +31,84 @@ public function isStaticMethodSupported(MethodReflection $methodReflection): boo
29
31
return $ methodReflection ->getName () === 'makeInstance ' ;
30
32
}
31
33
32
- public function getTypeFromStaticMethodCall (MethodReflection $ methodReflection , StaticCall $ methodCall , Scope $ scope ): Type
34
+ public function getTypeFromStaticMethodCall (MethodReflection $ methodReflection , StaticCall $ methodCall , Scope $ scope ): TypeInterface
33
35
{
34
- if (empty ($ methodCall ->args )) {
36
+ try {
37
+ $ classNameArgument = $ this ->fetchClassNameArgument ($ methodCall );
38
+ $ classNameArgumentValueExpression = $ classNameArgument ->value ;
39
+
40
+ switch (true ) {
41
+ case $ classNameArgumentValueExpression instanceof StringNode:
42
+ /*
43
+ * Examples:
44
+ *
45
+ * - GeneralUtility::makeInstance('foo')
46
+ * - GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler')
47
+ */
48
+ return $ this ->createObjectTypeFromString ($ classNameArgumentValueExpression );
49
+ case $ classNameArgumentValueExpression instanceof ClassConstFetch:
50
+ /*
51
+ * Examples:
52
+ *
53
+ * - GeneralUtility::makeInstance(TYPO3\CMS\Core\DataHandling\DataHandler::class)
54
+ * - GeneralUtility::makeInstance(self::class)
55
+ * - GeneralUtility::makeInstance(static::class)
56
+ */
57
+ return $ this ->createObjectTypeFromClassConstFetch ($ classNameArgumentValueExpression , $ scope ->getClassReflection ());
58
+ default :
59
+ throw new \InvalidArgumentException (
60
+ 'Argument $className is neither a string nor a class constant ' ,
61
+ 1584879239
62
+ );
63
+ }
64
+ } catch (\Throwable $ exception ) {
35
65
return new ObjectWithoutClassType ();
36
66
}
67
+ }
37
68
38
- /** @var Arg $argument */
39
- $ argument = $ methodCall ->args [0 ];
69
+ private function fetchClassNameArgument (StaticCall $ methodCall ): ArgumentNode
70
+ {
71
+ if (empty ($ methodCall ->args )) {
72
+ /*
73
+ * This usually does not happen as calling GeneralUtility::makeInstance() without the mandatory argument
74
+ * $className results in a syntax error.
75
+ */
76
+ throw new \LogicException ('Method makeInstance is called without arguments. ' , 1584878263 );
77
+ }
40
78
41
- /** @var Expr $argumentValue */
42
- $ argumentValue = $ argument -> value ;
79
+ return $ methodCall -> args [ 0 ];
80
+ }
43
81
44
- if (!$ argumentValue instanceof ClassConstFetch) {
45
- return new ObjectWithoutClassType ();
82
+ private function createObjectTypeFromString (StringNode $ string ): TypeInterface
83
+ {
84
+ $ className = $ string ->value ;
85
+
86
+ if (!class_exists ($ className )) {
87
+ throw new \LogicException ('makeInstance has been called with non class name string ' , 1584879581 );
46
88
}
47
- /** @var ClassConstFetch $argumentValue */
48
89
49
- $ class = $ argumentValue ->class ;
90
+ return new ObjectType ($ className );
91
+ }
50
92
93
+ private function createObjectTypeFromClassConstFetch (ClassConstFetch $ expression , ?ClassReflection $ classReflection ): TypeInterface
94
+ {
95
+ $ class = $ expression ->class ;
51
96
if (!$ class instanceof Name) {
52
- return new ObjectWithoutClassType ( );
97
+ throw new \ LogicException ( '' , 1584878823 );
53
98
}
54
99
/** @var Name $class */
55
100
56
101
$ className = $ class ->toString ();
57
102
103
+ if ($ className === 'self ' && $ classReflection !== null ) {
104
+ return new ObjectType ($ classReflection ->getName ());
105
+ }
106
+
107
+ if ($ className === 'static ' && $ classReflection !== null ) {
108
+ $ callingClass = $ classReflection ->getName ();
109
+ return new StaticType ($ callingClass );
110
+ }
111
+
58
112
return new ObjectType ($ className );
59
113
}
60
114
}
0 commit comments