Issues (14)

src/Reflection/Reflection.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\Name;
13
use PHPStan\Analyser\Scope;
14
use PHPStan\Reflection\ClassReflection;
15
use PHPStan\Reflection\ExtendedParameterReflection;
16
use PHPStan\Reflection\ReflectionProvider;
17
use Rector\NodeNameResolver\NodeNameResolver;
18
use Rector\NodeTypeResolver\Node\AttributeKey;
19
use Rector\NodeTypeResolver\NodeTypeResolver;
20
use ReflectionException;
21
use ReflectionFunction;
22
use ReflectionFunctionAbstract;
23
24
use function in_array;
25
26
final readonly class Reflection
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_READONLY, expecting T_CLASS on line 26 at column 6
Loading history...
27
{
28
    private ParameterReflection $parameterReflection;
29
30 1
    public function __construct(
31
        private ReflectionProvider $reflectionProvider,
32
        private NodeNameResolver $nodeNameResolver,
33
        private NodeTypeResolver $nodeTypeResolver,
34
        ?ParameterReflection $parameterReflection = null,
35
    ) {
36 1
        if ($parameterReflection === null) {
37
            $parameterReflection = new ParameterReflection(
38
                reflectionProvider: $reflectionProvider,
39
                nodeNameResolver: $nodeNameResolver,
40
                nodeTypeResolver: $nodeTypeResolver,
41
            );
42
        }
43 1
        $this->parameterReflection = $parameterReflection;
44
    }
45
46
    /**
47
     * @return ExtendedParameterReflection[]
48
     */
49 13
    public function getParameters(Node $node): array
50
    {
51 13
        return $this->parameterReflection->getParameters(node: $node);
52
    }
53
54
    /**
55
     * Resolve the reflection for a function, method/static call, or constructor.
56
     */
57 8
    public static function getFunctionReflection(
58
        FuncCall|StaticCall|MethodCall|New_ $node,
59
        ?ClassReflection $classReflection,
60
    ): null|ReflectionFunctionAbstract|false {
61 8
        if (self::isFuncCall($node)) {
62 4
            return self::resolveFunction($node);
63
        }
64
65 4
        if (self::isMethodOrStaticCall($node, $classReflection)) {
66 3
            return self::resolveMethod($node, $classReflection);
67
        }
68
69 3
        if (self::isConstructorCall($node, $classReflection)) {
70 3
            return self::resolveConstructor($classReflection);
71
        }
72
73
        return null;
74
    }
75
76 8
    private static function isFuncCall(Node $node): bool
77
    {
78 8
        return $node instanceof FuncCall && $node->name instanceof Name;
79
    }
80
81 4
    private static function isMethodOrStaticCall(Node $node, ?ClassReflection $classReflection): bool
82
    {
83 4
        return ($node instanceof MethodCall || $node instanceof StaticCall)
84 4
            && $classReflection !== null
85 4
            && $node->name instanceof Node\Identifier;
86
    }
87
88 3
    private static function isConstructorCall(Node $node, ?ClassReflection $classReflection): bool
89
    {
90 3
        return $node instanceof New_ && $classReflection !== null;
91
    }
92
93 4
    private static function resolveFunction(FuncCall $node): ?ReflectionFunctionAbstract
94
    {
95
        try {
96 4
            return new ReflectionFunction((string) $node->name);
97
        } catch (ReflectionException) {
98
            return null;
99
        }
100
    }
101
102 3
    private static function resolveMethod(
103
        MethodCall|StaticCall $node,
104
        ClassReflection $classReflection,
105
    ): null|ReflectionFunctionAbstract|false {
106 3
        $methodName = $node->name->name;
107
108
        try {
109 3
            $native = $classReflection->getNativeReflection();
110 3
            if (! $native->hasMethod($methodName)) {
111
                return null;
112
            }
113
114 3
            return $native->getMethod($methodName);
115
        } catch (ReflectionException) {
116
            return null;
117
        }
118
    }
119
120 3
    private static function resolveConstructor(ClassReflection $classReflection): ?ReflectionFunctionAbstract
121
    {
122
        try {
123 3
            return $classReflection->getNativeReflection()->getConstructor();
124
        } catch (ReflectionException) {
125
            return null;
126
        }
127
    }
128
129 13
    public function getClassReflection(FuncCall|StaticCall|MethodCall|New_ $node): ?ClassReflection
130
    {
131 13
        if ($node instanceof MethodCall) {
132 3
            $callerType = $this->nodeTypeResolver->getType(node: $node->var);
133 3
            $classReflections = $callerType->getObjectClassReflections();
134
135 3
            return $classReflections[0] ?? null;
136
        }
137
138 12
        if ($node instanceof StaticCall && $node->class instanceof Name) {
139 2
            return $this->fetchClass($node->class, $node);
140
        }
141
142 11
        if ($node instanceof New_ && $node->class instanceof Name) {
143 4
            return $this->fetchClass($node->class, $node);
144
        }
145
146 7
        return null;
147
    }
148
149 5
    private function fetchClass(Name $name, Node $contextNode): ?ClassReflection
150
    {
151 5
        $className = $this->nodeNameResolver->getName(node: $name);
152 5
        if ($className === null) {
153
            return null;
154
        }
155
156 5
        $scope = $contextNode->getAttribute(key: AttributeKey::SCOPE);
157 5
        if ($scope instanceof Scope) {
158 5
            $lowerClassName = strtolower($className);
159
160 5
            if (in_array($lowerClassName, ['self', 'static'], true)) {
161 1
                return $scope->getClassReflection();
162
            }
163
164 5
            if ($lowerClassName === 'parent') {
165
                return $scope->getClassReflection()?->getParentClass();
166
            }
167
        }
168
169 5
        return $this->reflectionProvider->hasClass($className)
170 5
            ? $this->reflectionProvider->getClass($className)
171 5
            : null;
172
    }
173
}
174