Issues (12)

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\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
A parse error occurred: Syntax error, unexpected T_READONLY, expecting T_CLASS on line 22 at column 6
Loading history...
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