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: | |