| 1: | <?php declare(strict_types=1); |
| 2: | |
| 3: | namespace PhpParser\NodeVisitor; |
| 4: | |
| 5: | use PhpParser\ErrorHandler; |
| 6: | use PhpParser\NameContext; |
| 7: | use PhpParser\Node; |
| 8: | use PhpParser\Node\Expr; |
| 9: | use PhpParser\Node\Name; |
| 10: | use PhpParser\Node\Name\FullyQualified; |
| 11: | use PhpParser\Node\Stmt; |
| 12: | use PhpParser\NodeVisitorAbstract; |
| 13: | |
| 14: | class NameResolver extends NodeVisitorAbstract { |
| 15: | |
| 16: | protected NameContext $nameContext; |
| 17: | |
| 18: | |
| 19: | protected bool $preserveOriginalNames; |
| 20: | |
| 21: | |
| 22: | protected bool $replaceNodes; |
| 23: | |
| 24: | |
| 25: | |
| 26: | |
| 27: | |
| 28: | |
| 29: | |
| 30: | |
| 31: | |
| 32: | |
| 33: | |
| 34: | |
| 35: | |
| 36: | |
| 37: | public function __construct(?ErrorHandler $errorHandler = null, array $options = []) { |
| 38: | $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing()); |
| 39: | $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; |
| 40: | $this->replaceNodes = $options['replaceNodes'] ?? true; |
| 41: | } |
| 42: | |
| 43: | |
| 44: | |
| 45: | |
| 46: | public function getNameContext(): NameContext { |
| 47: | return $this->nameContext; |
| 48: | } |
| 49: | |
| 50: | public function beforeTraverse(array $nodes): ?array { |
| 51: | $this->nameContext->startNamespace(); |
| 52: | return null; |
| 53: | } |
| 54: | |
| 55: | public function enterNode(Node $node) { |
| 56: | if ($node instanceof Stmt\Namespace_) { |
| 57: | $this->nameContext->startNamespace($node->name); |
| 58: | } elseif ($node instanceof Stmt\Use_) { |
| 59: | foreach ($node->uses as $use) { |
| 60: | $this->addAlias($use, $node->type, null); |
| 61: | } |
| 62: | } elseif ($node instanceof Stmt\GroupUse) { |
| 63: | foreach ($node->uses as $use) { |
| 64: | $this->addAlias($use, $node->type, $node->prefix); |
| 65: | } |
| 66: | } elseif ($node instanceof Stmt\Class_) { |
| 67: | if (null !== $node->extends) { |
| 68: | $node->extends = $this->resolveClassName($node->extends); |
| 69: | } |
| 70: | |
| 71: | foreach ($node->implements as &$interface) { |
| 72: | $interface = $this->resolveClassName($interface); |
| 73: | } |
| 74: | |
| 75: | $this->resolveAttrGroups($node); |
| 76: | if (null !== $node->name) { |
| 77: | $this->addNamespacedName($node); |
| 78: | } else { |
| 79: | $node->namespacedName = null; |
| 80: | } |
| 81: | } elseif ($node instanceof Stmt\Interface_) { |
| 82: | foreach ($node->extends as &$interface) { |
| 83: | $interface = $this->resolveClassName($interface); |
| 84: | } |
| 85: | |
| 86: | $this->resolveAttrGroups($node); |
| 87: | $this->addNamespacedName($node); |
| 88: | } elseif ($node instanceof Stmt\Enum_) { |
| 89: | foreach ($node->implements as &$interface) { |
| 90: | $interface = $this->resolveClassName($interface); |
| 91: | } |
| 92: | |
| 93: | $this->resolveAttrGroups($node); |
| 94: | $this->addNamespacedName($node); |
| 95: | } elseif ($node instanceof Stmt\Trait_) { |
| 96: | $this->resolveAttrGroups($node); |
| 97: | $this->addNamespacedName($node); |
| 98: | } elseif ($node instanceof Stmt\Function_) { |
| 99: | $this->resolveSignature($node); |
| 100: | $this->resolveAttrGroups($node); |
| 101: | $this->addNamespacedName($node); |
| 102: | } elseif ($node instanceof Stmt\ClassMethod |
| 103: | || $node instanceof Expr\Closure |
| 104: | || $node instanceof Expr\ArrowFunction |
| 105: | ) { |
| 106: | $this->resolveSignature($node); |
| 107: | $this->resolveAttrGroups($node); |
| 108: | } elseif ($node instanceof Stmt\Property) { |
| 109: | if (null !== $node->type) { |
| 110: | $node->type = $this->resolveType($node->type); |
| 111: | } |
| 112: | $this->resolveAttrGroups($node); |
| 113: | } elseif ($node instanceof Node\PropertyHook) { |
| 114: | foreach ($node->params as $param) { |
| 115: | $param->type = $this->resolveType($param->type); |
| 116: | $this->resolveAttrGroups($param); |
| 117: | } |
| 118: | $this->resolveAttrGroups($node); |
| 119: | } elseif ($node instanceof Stmt\Const_) { |
| 120: | foreach ($node->consts as $const) { |
| 121: | $this->addNamespacedName($const); |
| 122: | } |
| 123: | $this->resolveAttrGroups($node); |
| 124: | } elseif ($node instanceof Stmt\ClassConst) { |
| 125: | if (null !== $node->type) { |
| 126: | $node->type = $this->resolveType($node->type); |
| 127: | } |
| 128: | $this->resolveAttrGroups($node); |
| 129: | } elseif ($node instanceof Stmt\EnumCase) { |
| 130: | $this->resolveAttrGroups($node); |
| 131: | } elseif ($node instanceof Expr\StaticCall |
| 132: | || $node instanceof Expr\StaticPropertyFetch |
| 133: | || $node instanceof Expr\ClassConstFetch |
| 134: | || $node instanceof Expr\New_ |
| 135: | || $node instanceof Expr\Instanceof_ |
| 136: | ) { |
| 137: | if ($node->class instanceof Name) { |
| 138: | $node->class = $this->resolveClassName($node->class); |
| 139: | } |
| 140: | } elseif ($node instanceof Stmt\Catch_) { |
| 141: | foreach ($node->types as &$type) { |
| 142: | $type = $this->resolveClassName($type); |
| 143: | } |
| 144: | } elseif ($node instanceof Expr\FuncCall) { |
| 145: | if ($node->name instanceof Name) { |
| 146: | $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); |
| 147: | } |
| 148: | } elseif ($node instanceof Expr\ConstFetch) { |
| 149: | $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); |
| 150: | } elseif ($node instanceof Stmt\TraitUse) { |
| 151: | foreach ($node->traits as &$trait) { |
| 152: | $trait = $this->resolveClassName($trait); |
| 153: | } |
| 154: | |
| 155: | foreach ($node->adaptations as $adaptation) { |
| 156: | if (null !== $adaptation->trait) { |
| 157: | $adaptation->trait = $this->resolveClassName($adaptation->trait); |
| 158: | } |
| 159: | |
| 160: | if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { |
| 161: | foreach ($adaptation->insteadof as &$insteadof) { |
| 162: | $insteadof = $this->resolveClassName($insteadof); |
| 163: | } |
| 164: | } |
| 165: | } |
| 166: | } |
| 167: | |
| 168: | return null; |
| 169: | } |
| 170: | |
| 171: | |
| 172: | private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void { |
| 173: | |
| 174: | $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; |
| 175: | |
| 176: | $type |= $use->type; |
| 177: | |
| 178: | $this->nameContext->addAlias( |
| 179: | $name, (string) $use->getAlias(), $type, $use->getAttributes() |
| 180: | ); |
| 181: | } |
| 182: | |
| 183: | |
| 184: | private function resolveSignature($node): void { |
| 185: | foreach ($node->params as $param) { |
| 186: | $param->type = $this->resolveType($param->type); |
| 187: | $this->resolveAttrGroups($param); |
| 188: | } |
| 189: | $node->returnType = $this->resolveType($node->returnType); |
| 190: | } |
| 191: | |
| 192: | |
| 193: | |
| 194: | |
| 195: | |
| 196: | |
| 197: | private function resolveType(?Node $node): ?Node { |
| 198: | if ($node instanceof Name) { |
| 199: | return $this->resolveClassName($node); |
| 200: | } |
| 201: | if ($node instanceof Node\NullableType) { |
| 202: | $node->type = $this->resolveType($node->type); |
| 203: | return $node; |
| 204: | } |
| 205: | if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) { |
| 206: | foreach ($node->types as &$type) { |
| 207: | $type = $this->resolveType($type); |
| 208: | } |
| 209: | return $node; |
| 210: | } |
| 211: | return $node; |
| 212: | } |
| 213: | |
| 214: | |
| 215: | |
| 216: | |
| 217: | |
| 218: | |
| 219: | |
| 220: | |
| 221: | |
| 222: | protected function resolveName(Name $name, int $type): Name { |
| 223: | if (!$this->replaceNodes) { |
| 224: | $resolvedName = $this->nameContext->getResolvedName($name, $type); |
| 225: | if (null !== $resolvedName) { |
| 226: | $name->setAttribute('resolvedName', $resolvedName); |
| 227: | } else { |
| 228: | $name->setAttribute('namespacedName', FullyQualified::concat( |
| 229: | $this->nameContext->getNamespace(), $name, $name->getAttributes())); |
| 230: | } |
| 231: | return $name; |
| 232: | } |
| 233: | |
| 234: | if ($this->preserveOriginalNames) { |
| 235: | |
| 236: | $originalName = $name; |
| 237: | $name = clone $originalName; |
| 238: | $name->setAttribute('originalName', $originalName); |
| 239: | } |
| 240: | |
| 241: | $resolvedName = $this->nameContext->getResolvedName($name, $type); |
| 242: | if (null !== $resolvedName) { |
| 243: | return $resolvedName; |
| 244: | } |
| 245: | |
| 246: | |
| 247: | |
| 248: | $name->setAttribute('namespacedName', FullyQualified::concat( |
| 249: | $this->nameContext->getNamespace(), $name, $name->getAttributes())); |
| 250: | return $name; |
| 251: | } |
| 252: | |
| 253: | protected function resolveClassName(Name $name): Name { |
| 254: | return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); |
| 255: | } |
| 256: | |
| 257: | protected function addNamespacedName(Node $node): void { |
| 258: | $node->namespacedName = Name::concat( |
| 259: | $this->nameContext->getNamespace(), (string) $node->name); |
| 260: | } |
| 261: | |
| 262: | protected function resolveAttrGroups(Node $node): void { |
| 263: | foreach ($node->attrGroups as $attrGroup) { |
| 264: | foreach ($attrGroup->attrs as $attr) { |
| 265: | $attr->name = $this->resolveClassName($attr->name); |
| 266: | } |
| 267: | } |
| 268: | } |
| 269: | } |
| 270: | |