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