PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
betterdocs / includes / Dependencies / PhpParser / NodeVisitor / NameResolver.php

NameResolver.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.8.9, at includes/Dependencies/PhpParser/NodeVisitor/NameResolver.php

273 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Dependencies\PhpParser\NodeVisitor;
4
5 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Error;
6 use WPDeveloper\BetterDocs\Dependencies\PhpParser\ErrorHandler;
7 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node;
8 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr;
9 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Name;
10 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Name\FullyQualified;
11 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt;
12 use WPDeveloper\BetterDocs\Dependencies\PhpParser\NodeVisitorAbstract;
13
14 class NameResolver extends NodeVisitorAbstract
15 {
16 /** @var null|Name Current namespace */
17 protected $namespace;
18
19 /** @var array Map of format [aliasType => [aliasName => originalName]] */
20 protected $aliases;
21
22 /** @var ErrorHandler Error handler */
23 protected $errorHandler;
24
25 /** @var bool Whether to preserve original names */
26 protected $preserveOriginalNames;
27
28 /**
29 * Constructs a name resolution visitor.
30 *
31 * Options: If "preserveOriginalNames" is enabled, an "originalName" attribute will be added to
32 * all name nodes that underwent resolution.
33 *
34 * @param ErrorHandler|null $errorHandler Error handler
35 * @param array $options Options
36 */
37 public function __construct(ErrorHandler $errorHandler = null, array $options = []) {
38 $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
39 $this->preserveOriginalNames = !empty($options['preserveOriginalNames']);
40 }
41
42 public function beforeTraverse(array $nodes) {
43 $this->resetState();
44 }
45
46 public function enterNode(Node $node) {
47 if ($node instanceof Stmt\Namespace_) {
48 $this->resetState($node->name);
49 } elseif ($node instanceof Stmt\Use_) {
50 foreach ($node->uses as $use) {
51 $this->addAlias($use, $node->type, null);
52 }
53 } elseif ($node instanceof Stmt\GroupUse) {
54 foreach ($node->uses as $use) {
55 $this->addAlias($use, $node->type, $node->prefix);
56 }
57 } elseif ($node instanceof Stmt\Class_) {
58 if (null !== $node->extends) {
59 $node->extends = $this->resolveClassName($node->extends);
60 }
61
62 foreach ($node->implements as &$interface) {
63 $interface = $this->resolveClassName($interface);
64 }
65
66 if (null !== $node->name) {
67 $this->addNamespacedName($node);
68 }
69 } elseif ($node instanceof Stmt\Interface_) {
70 foreach ($node->extends as &$interface) {
71 $interface = $this->resolveClassName($interface);
72 }
73
74 $this->addNamespacedName($node);
75 } elseif ($node instanceof Stmt\Trait_) {
76 $this->addNamespacedName($node);
77 } elseif ($node instanceof Stmt\Function_) {
78 $this->addNamespacedName($node);
79 $this->resolveSignature($node);
80 } elseif ($node instanceof Stmt\ClassMethod
81 || $node instanceof Expr\Closure
82 ) {
83 $this->resolveSignature($node);
84 } elseif ($node instanceof Stmt\Const_) {
85 foreach ($node->consts as $const) {
86 $this->addNamespacedName($const);
87 }
88 } elseif ($node instanceof Expr\StaticCall
89 || $node instanceof Expr\StaticPropertyFetch
90 || $node instanceof Expr\ClassConstFetch
91 || $node instanceof Expr\New_
92 || $node instanceof Expr\Instanceof_
93 ) {
94 if ($node->class instanceof Name) {
95 $node->class = $this->resolveClassName($node->class);
96 }
97 } elseif ($node instanceof Stmt\Catch_) {
98 foreach ($node->types as &$type) {
99 $type = $this->resolveClassName($type);
100 }
101 } elseif ($node instanceof Expr\FuncCall) {
102 if ($node->name instanceof Name) {
103 $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_FUNCTION);
104 }
105 } elseif ($node instanceof Expr\ConstFetch) {
106 $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_CONSTANT);
107 } elseif ($node instanceof Stmt\TraitUse) {
108 foreach ($node->traits as &$trait) {
109 $trait = $this->resolveClassName($trait);
110 }
111
112 foreach ($node->adaptations as $adaptation) {
113 if (null !== $adaptation->trait) {
114 $adaptation->trait = $this->resolveClassName($adaptation->trait);
115 }
116
117 if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
118 foreach ($adaptation->insteadof as &$insteadof) {
119 $insteadof = $this->resolveClassName($insteadof);
120 }
121 }
122 }
123 }
124 }
125
126 protected function resetState(Name $namespace = null) {
127 $this->namespace = $namespace;
128 $this->aliases = array(
129 Stmt\Use_::TYPE_NORMAL => array(),
130 Stmt\Use_::TYPE_FUNCTION => array(),
131 Stmt\Use_::TYPE_CONSTANT => array(),
132 );
133 }
134
135 protected function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
136 // Add prefix for group uses
137 $name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
138 // Type is determined either by individual element or whole use declaration
139 $type |= $use->type;
140
141 // Constant names are case sensitive, everything else case insensitive
142 if ($type === Stmt\Use_::TYPE_CONSTANT) {
143 $aliasName = $use->alias;
144 } else {
145 $aliasName = strtolower($use->alias);
146 }
147
148 if (isset($this->aliases[$type][$aliasName])) {
149 $typeStringMap = array(
150 Stmt\Use_::TYPE_NORMAL => '',
151 Stmt\Use_::TYPE_FUNCTION => 'function ',
152 Stmt\Use_::TYPE_CONSTANT => 'const ',
153 );
154
155 $this->errorHandler->handleError(new Error(
156 sprintf(
157 'Cannot use %s%s as %s because the name is already in use',
158 $typeStringMap[$type], $name, $use->alias
159 ),
160 $use->getAttributes()
161 ));
162 return;
163 }
164
165 $this->aliases[$type][$aliasName] = $name;
166 }
167
168 /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */
169 private function resolveSignature($node) {
170 foreach ($node->params as $param) {
171 $param->type = $this->resolveType($param->type);
172 }
173 $node->returnType = $this->resolveType($node->returnType);
174 }
175
176 private function resolveType($node) {
177 if ($node instanceof Node\NullableType) {
178 $node->type = $this->resolveType($node->type);
179 return $node;
180 }
181 if ($node instanceof Name) {
182 return $this->resolveClassName($node);
183 }
184 return $node;
185 }
186
187 protected function resolveClassName(Name $name) {
188 if ($this->preserveOriginalNames) {
189 // Save the original name
190 $originalName = $name;
191 $name = clone $originalName;
192 $name->setAttribute('originalName', $originalName);
193 }
194
195 // don't resolve special class names
196 if (in_array(strtolower($name->toString()), array('self', 'parent', 'static'))) {
197 if (!$name->isUnqualified()) {
198 $this->errorHandler->handleError(new Error(
199 sprintf("'\\%s' is an invalid class name", $name->toString()),
200 $name->getAttributes()
201 ));
202 }
203 return $name;
204 }
205
206 // fully qualified names are already resolved
207 if ($name->isFullyQualified()) {
208 return $name;
209 }
210
211 $aliasName = strtolower($name->getFirst());
212 if (!$name->isRelative() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) {
213 // resolve aliases (for non-relative names)
214 $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName];
215 return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
216 }
217
218 // if no alias exists prepend current namespace
219 return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
220 }
221
222 protected function resolveOtherName(Name $name, $type) {
223 if ($this->preserveOriginalNames) {
224 // Save the original name
225 $originalName = $name;
226 $name = clone $originalName;
227 $name->setAttribute('originalName', $originalName);
228 }
229
230 // fully qualified names are already resolved
231 if ($name->isFullyQualified()) {
232 return $name;
233 }
234
235 // resolve aliases for qualified names
236 $aliasName = strtolower($name->getFirst());
237 if ($name->isQualified() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) {
238 $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName];
239 return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
240 }
241
242 if ($name->isUnqualified()) {
243 if ($type === Stmt\Use_::TYPE_CONSTANT) {
244 // constant aliases are case-sensitive, function aliases case-insensitive
245 $aliasName = $name->getFirst();
246 }
247
248 if (isset($this->aliases[$type][$aliasName])) {
249 // resolve unqualified aliases
250 return new FullyQualified($this->aliases[$type][$aliasName], $name->getAttributes());
251 }
252
253 if (null === $this->namespace) {
254 // outside of a namespace unaliased unqualified is same as fully qualified
255 return new FullyQualified($name, $name->getAttributes());
256 }
257
258 // unqualified names inside a namespace cannot be resolved at compile-time
259 // add the namespaced version of the name as an attribute
260 $name->setAttribute('namespacedName',
261 FullyQualified::concat($this->namespace, $name, $name->getAttributes()));
262 return $name;
263 }
264
265 // if no alias exists prepend current namespace
266 return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
267 }
268
269 protected function addNamespacedName(Node $node) {
270 $node->namespacedName = Name::concat($this->namespace, $node->name);
271 }
272 }
273