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