PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.5.1
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.5.1
0.5.7 0.5.6 0.5.5 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.9 0.4.8 0.4.7 0.4.6 trunk 0.0.1 0.0.2 0.2.8 0.2.9 0.3.0 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 All 33 releases
code-engine / vendor / nikic / php-parser / lib / PhpParser / NameContext.php

NameContext.php in Code Engine – PHP Snippets, AI Functions & Automation for WordPress 0.5.1, at vendor/nikic/php-parser/lib/PhpParser/NameContext.php

285 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types=1);
2
3 namespace PhpParser;
4
5 use PhpParser\Node\Name;
6 use PhpParser\Node\Name\FullyQualified;
7 use PhpParser\Node\Stmt;
8
9 class NameContext {
10 /** @var null|Name Current namespace */
11 protected ?Name $namespace;
12
13 /** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */
14 protected array $aliases = [];
15
16 /** @var Name[][] Same as $aliases but preserving original case */
17 protected array $origAliases = [];
18
19 /** @var ErrorHandler Error handler */
20 protected ErrorHandler $errorHandler;
21
22 /**
23 * Create a name context.
24 *
25 * @param ErrorHandler $errorHandler Error handling used to report errors
26 */
27 public function __construct(ErrorHandler $errorHandler) {
28 $this->errorHandler = $errorHandler;
29 }
30
31 /**
32 * Start a new namespace.
33 *
34 * This also resets the alias table.
35 *
36 * @param Name|null $namespace Null is the global namespace
37 */
38 public function startNamespace(?Name $namespace = null): void {
39 $this->namespace = $namespace;
40 $this->origAliases = $this->aliases = [
41 Stmt\Use_::TYPE_NORMAL => [],
42 Stmt\Use_::TYPE_FUNCTION => [],
43 Stmt\Use_::TYPE_CONSTANT => [],
44 ];
45 }
46
47 /**
48 * Add an alias / import.
49 *
50 * @param Name $name Original name
51 * @param string $aliasName Aliased name
52 * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
53 * @param array<string, mixed> $errorAttrs Attributes to use to report an error
54 */
55 public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []): void {
56 // Constant names are case sensitive, everything else case insensitive
57 if ($type === Stmt\Use_::TYPE_CONSTANT) {
58 $aliasLookupName = $aliasName;
59 } else {
60 $aliasLookupName = strtolower($aliasName);
61 }
62
63 if (isset($this->aliases[$type][$aliasLookupName])) {
64 $typeStringMap = [
65 Stmt\Use_::TYPE_NORMAL => '',
66 Stmt\Use_::TYPE_FUNCTION => 'function ',
67 Stmt\Use_::TYPE_CONSTANT => 'const ',
68 ];
69
70 $this->errorHandler->handleError(new Error(
71 sprintf(
72 'Cannot use %s%s as %s because the name is already in use',
73 $typeStringMap[$type], $name, $aliasName
74 ),
75 $errorAttrs
76 ));
77 return;
78 }
79
80 $this->aliases[$type][$aliasLookupName] = $name;
81 $this->origAliases[$type][$aliasName] = $name;
82 }
83
84 /**
85 * Get current namespace.
86 *
87 * @return null|Name Namespace (or null if global namespace)
88 */
89 public function getNamespace(): ?Name {
90 return $this->namespace;
91 }
92
93 /**
94 * Get resolved name.
95 *
96 * @param Name $name Name to resolve
97 * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT}
98 *
99 * @return null|Name Resolved name, or null if static resolution is not possible
100 */
101 public function getResolvedName(Name $name, int $type): ?Name {
102 // don't resolve special class names
103 if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) {
104 if (!$name->isUnqualified()) {
105 $this->errorHandler->handleError(new Error(
106 sprintf("'\\%s' is an invalid class name", $name->toString()),
107 $name->getAttributes()
108 ));
109 }
110 return $name;
111 }
112
113 // fully qualified names are already resolved
114 if ($name->isFullyQualified()) {
115 return $name;
116 }
117
118 // Try to resolve aliases
119 if (null !== $resolvedName = $this->resolveAlias($name, $type)) {
120 return $resolvedName;
121 }
122
123 if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) {
124 if (null === $this->namespace) {
125 // outside of a namespace unaliased unqualified is same as fully qualified
126 return new FullyQualified($name, $name->getAttributes());
127 }
128
129 // Cannot resolve statically
130 return null;
131 }
132
133 // if no alias exists prepend current namespace
134 return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
135 }
136
137 /**
138 * Get resolved class name.
139 *
140 * @param Name $name Class ame to resolve
141 *
142 * @return Name Resolved name
143 */
144 public function getResolvedClassName(Name $name): Name {
145 return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL);
146 }
147
148 /**
149 * Get possible ways of writing a fully qualified name (e.g., by making use of aliases).
150 *
151 * @param string $name Fully-qualified name (without leading namespace separator)
152 * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
153 *
154 * @return Name[] Possible representations of the name
155 */
156 public function getPossibleNames(string $name, int $type): array {
157 $lcName = strtolower($name);
158
159 if ($type === Stmt\Use_::TYPE_NORMAL) {
160 // self, parent and static must always be unqualified
161 if ($lcName === "self" || $lcName === "parent" || $lcName === "static") {
162 return [new Name($name)];
163 }
164 }
165
166 // Collect possible ways to write this name, starting with the fully-qualified name
167 $possibleNames = [new FullyQualified($name)];
168
169 if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) {
170 // Make sure there is no alias that makes the normally namespace-relative name
171 // into something else
172 if (null === $this->resolveAlias($nsRelativeName, $type)) {
173 $possibleNames[] = $nsRelativeName;
174 }
175 }
176
177 // Check for relevant namespace use statements
178 foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) {
179 $lcOrig = $orig->toLowerString();
180 if (0 === strpos($lcName, $lcOrig . '\\')) {
181 $possibleNames[] = new Name($alias . substr($name, strlen($lcOrig)));
182 }
183 }
184
185 // Check for relevant type-specific use statements
186 foreach ($this->origAliases[$type] as $alias => $orig) {
187 if ($type === Stmt\Use_::TYPE_CONSTANT) {
188 // Constants are are complicated-sensitive
189 $normalizedOrig = $this->normalizeConstName($orig->toString());
190 if ($normalizedOrig === $this->normalizeConstName($name)) {
191 $possibleNames[] = new Name($alias);
192 }
193 } else {
194 // Everything else is case-insensitive
195 if ($orig->toLowerString() === $lcName) {
196 $possibleNames[] = new Name($alias);
197 }
198 }
199 }
200
201 return $possibleNames;
202 }
203
204 /**
205 * Get shortest representation of this fully-qualified name.
206 *
207 * @param string $name Fully-qualified name (without leading namespace separator)
208 * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
209 *
210 * @return Name Shortest representation
211 */
212 public function getShortName(string $name, int $type): Name {
213 $possibleNames = $this->getPossibleNames($name, $type);
214
215 // Find shortest name
216 $shortestName = null;
217 $shortestLength = \INF;
218 foreach ($possibleNames as $possibleName) {
219 $length = strlen($possibleName->toCodeString());
220 if ($length < $shortestLength) {
221 $shortestName = $possibleName;
222 $shortestLength = $length;
223 }
224 }
225
226 return $shortestName;
227 }
228
229 private function resolveAlias(Name $name, int $type): ?FullyQualified {
230 $firstPart = $name->getFirst();
231
232 if ($name->isQualified()) {
233 // resolve aliases for qualified names, always against class alias table
234 $checkName = strtolower($firstPart);
235 if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) {
236 $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName];
237 return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
238 }
239 } elseif ($name->isUnqualified()) {
240 // constant aliases are case-sensitive, function aliases case-insensitive
241 $checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : strtolower($firstPart);
242 if (isset($this->aliases[$type][$checkName])) {
243 // resolve unqualified aliases
244 return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes());
245 }
246 }
247
248 // No applicable aliases
249 return null;
250 }
251
252 private function getNamespaceRelativeName(string $name, string $lcName, int $type): ?Name {
253 if (null === $this->namespace) {
254 return new Name($name);
255 }
256
257 if ($type === Stmt\Use_::TYPE_CONSTANT) {
258 // The constants true/false/null always resolve to the global symbols, even inside a
259 // namespace, so they may be used without qualification
260 if ($lcName === "true" || $lcName === "false" || $lcName === "null") {
261 return new Name($name);
262 }
263 }
264
265 $namespacePrefix = strtolower($this->namespace . '\\');
266 if (0 === strpos($lcName, $namespacePrefix)) {
267 return new Name(substr($name, strlen($namespacePrefix)));
268 }
269
270 return null;
271 }
272
273 private function normalizeConstName(string $name): string {
274 $nsSep = strrpos($name, '\\');
275 if (false === $nsSep) {
276 return $name;
277 }
278
279 // Constants have case-insensitive namespace and case-sensitive short-name
280 $ns = substr($name, 0, $nsSep);
281 $shortName = substr($name, $nsSep + 1);
282 return strtolower($ns) . '\\' . $shortName;
283 }
284 }
285