Issues (14)

src/Reflection/ParameterReflection.php (1 issue)

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