savinmikhail /
AddNamedArgumentsRector
| 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
Bug
introduced
by
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 |