1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace SavinMikhail\AddNamedArgumentsRector\Reflection; |
||
6 | |||
7 | use PhpParser\Node; |
||
8 | use PhpParser\Node\Expr\FuncCall; |
||
9 | use PhpParser\Node\Expr\MethodCall; |
||
10 | use PhpParser\Node\Expr\New_; |
||
11 | use PhpParser\Node\Expr\StaticCall; |
||
12 | use PhpParser\Node\Identifier; |
||
13 | use PhpParser\Node\Name; |
||
14 | use PHPStan\Reflection\ClassMemberAccessAnswerer; |
||
15 | use PHPStan\Reflection\ExtendedParameterReflection; |
||
16 | use PHPStan\Reflection\ReflectionProvider; |
||
17 | use PHPStan\ShouldNotHappenException; |
||
18 | use Rector\NodeNameResolver\NodeNameResolver; |
||
19 | use Rector\NodeTypeResolver\Node\AttributeKey; |
||
20 | use Rector\NodeTypeResolver\NodeTypeResolver; |
||
21 | |||
22 | final readonly class ParameterReflection |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
23 | { |
||
24 | 1 | public function __construct( |
|
25 | private ReflectionProvider $reflectionProvider, |
||
26 | protected NodeNameResolver $nodeNameResolver, |
||
27 | protected NodeTypeResolver $nodeTypeResolver, |
||
28 | 1 | ) {} |
|
29 | |||
30 | /** |
||
31 | * @return ExtendedParameterReflection[] |
||
32 | */ |
||
33 | 10 | public function getParameters(Node $node): array |
|
34 | { |
||
35 | 10 | $parameters = []; |
|
36 | |||
37 | 10 | if ($node instanceof New_) { |
|
38 | 3 | $parameters = $this->getConstructorArgs(node: $node); |
|
39 | 8 | } elseif ($node instanceof MethodCall) { |
|
40 | 2 | $parameters = $this->getMethodArgs(node: $node); |
|
41 | 6 | } elseif ($node instanceof StaticCall) { |
|
42 | 1 | $parameters = $this->getStaticMethodArgs(node: $node); |
|
43 | 5 | } elseif ($node instanceof FuncCall) { |
|
44 | 5 | $parameters = $this->getFuncArgs(node: $node); |
|
45 | } |
||
46 | |||
47 | 10 | return $parameters; |
|
48 | } |
||
49 | |||
50 | /** |
||
51 | * @return ExtendedParameterReflection[] |
||
52 | */ |
||
53 | 1 | private function getStaticMethodArgs(StaticCall $node): array |
|
54 | { |
||
55 | 1 | if (! $node->class instanceof Name) { |
|
56 | return []; |
||
57 | } |
||
58 | |||
59 | 1 | $className = $this->nodeNameResolver->getName(node: $node->class); |
|
60 | 1 | if (! $this->reflectionProvider->hasClass($className)) { |
|
61 | return []; |
||
62 | } |
||
63 | |||
64 | 1 | $classReflection = $this->reflectionProvider->getClass($className); |
|
65 | |||
66 | 1 | if ($node->name instanceof Identifier) { |
|
67 | 1 | $methodName = $node->name->name; |
|
68 | } elseif ($node->name instanceof Name) { |
||
69 | $methodName = (string) $node->name; |
||
70 | } else { |
||
71 | return []; |
||
72 | } |
||
73 | |||
74 | 1 | if (! $classReflection->hasMethod(methodName: $methodName)) { |
|
75 | return []; |
||
76 | } |
||
77 | |||
78 | /** @var ClassMemberAccessAnswerer $scope */ |
||
79 | 1 | $scope = $node->getAttribute(key: AttributeKey::SCOPE); |
|
80 | 1 | $reflection = $classReflection->getMethod(methodName: $methodName, scope: $scope); |
|
81 | |||
82 | try { |
||
83 | 1 | return $reflection |
|
84 | 1 | ->getOnlyVariant() |
|
85 | 1 | ->getParameters(); |
|
86 | } catch (ShouldNotHappenException) { |
||
87 | // for example in interface argument being called "$className" and in child class it being called "$entityName", |
||
88 | // we have no idea what will be resolved in a runtime, so just skip |
||
89 | return []; |
||
90 | } |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * @return ExtendedParameterReflection[] |
||
95 | */ |
||
96 | 2 | private function getMethodArgs(MethodCall $node): array |
|
97 | { |
||
98 | 2 | $callerType = $this->nodeTypeResolver->getType(node: $node->var); |
|
99 | |||
100 | 2 | $name = $node->name; |
|
101 | 2 | if ($name instanceof Node\Expr) { |
|
102 | return []; |
||
103 | } |
||
104 | 2 | $methodName = $name->name; |
|
105 | 2 | if (! $callerType->hasMethod($methodName)->yes()) { |
|
106 | return []; |
||
107 | } |
||
108 | |||
109 | /** @var ClassMemberAccessAnswerer $scope */ |
||
110 | 2 | $scope = $node->getAttribute(key: AttributeKey::SCOPE); |
|
111 | 2 | $reflection = $callerType->getMethod($methodName, $scope); |
|
112 | |||
113 | try { |
||
114 | 2 | return $reflection |
|
115 | 2 | ->getOnlyVariant() |
|
116 | 2 | ->getParameters(); |
|
117 | } catch (ShouldNotHappenException) { |
||
118 | // for example in interface argument being called "$className" and in child class it being called "$entityName", |
||
119 | // we have no idea what will be resolved in a runtime, so just skip |
||
120 | return []; |
||
121 | } |
||
122 | } |
||
123 | |||
124 | /** |
||
125 | * @return ExtendedParameterReflection[] |
||
126 | */ |
||
127 | 3 | private function getConstructorArgs(New_ $node): array |
|
128 | { |
||
129 | 3 | $calledName = $this->resolveCalledName(node: $node); |
|
130 | 3 | if ($calledName === null) { |
|
131 | return []; |
||
132 | } |
||
133 | |||
134 | 3 | if (! $this->reflectionProvider->hasClass($calledName)) { |
|
135 | return []; |
||
136 | } |
||
137 | 3 | $classReflection = $this->reflectionProvider->getClass($calledName); |
|
138 | |||
139 | 3 | if (! $classReflection->hasConstructor()) { |
|
140 | return []; |
||
141 | } |
||
142 | |||
143 | 3 | $reflection = $classReflection->getConstructor(); |
|
144 | |||
145 | try { |
||
146 | 3 | return $reflection |
|
147 | 3 | ->getOnlyVariant() |
|
148 | 3 | ->getParameters(); |
|
149 | 1 | } catch (ShouldNotHappenException) { |
|
150 | // for example in interface argument being called "$className" and in child class it being called "$entityName", |
||
151 | // we have no idea what will be resolved in a runtime, so just skip |
||
152 | 1 | return []; |
|
153 | } |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * @return ExtendedParameterReflection[] |
||
158 | */ |
||
159 | 5 | private function getFuncArgs(FuncCall $node): array |
|
160 | { |
||
161 | 5 | $calledName = $this->resolveCalledName(node: $node); |
|
162 | 5 | if ($calledName === null) { |
|
163 | return []; |
||
164 | } |
||
165 | |||
166 | 5 | $scope = $node->getAttribute(key: AttributeKey::SCOPE); |
|
167 | |||
168 | 5 | if (! $this->reflectionProvider->hasFunction(new Name(name: $calledName), $scope)) { |
|
169 | 1 | return []; |
|
170 | } |
||
171 | 4 | $reflection = $this->reflectionProvider->getFunction(new Name(name: $calledName), $scope); |
|
172 | |||
173 | try { |
||
174 | 4 | return $reflection |
|
175 | 4 | ->getOnlyVariant() |
|
176 | 4 | ->getParameters(); |
|
177 | } catch (ShouldNotHappenException) { |
||
178 | // for example in interface argument being called "$className" and in child class it being called "$entityName", |
||
179 | // we have no idea what will be resolved in a runtime, so just skip |
||
180 | return []; |
||
181 | } |
||
182 | } |
||
183 | |||
184 | 8 | private function resolveCalledName(Node $node): ?string |
|
185 | { |
||
186 | 8 | if ($node instanceof FuncCall && $node->name instanceof Name) { |
|
187 | 5 | return (string) $node->name; |
|
188 | } |
||
189 | |||
190 | 3 | if ($node instanceof MethodCall && $node->name instanceof Identifier) { |
|
191 | return (string) $node->name; |
||
192 | } |
||
193 | |||
194 | 3 | if ($node instanceof StaticCall && $node->name instanceof Identifier) { |
|
195 | return (string) $node->name; |
||
196 | } |
||
197 | |||
198 | 3 | if ($node instanceof New_ && $node->class instanceof Name) { |
|
199 | 3 | return (string) $node->class; |
|
200 | } |
||
201 | |||
202 | return null; |
||
203 | } |
||
204 | } |
||
205 |