Mercurial > hg > isophonics-drupal-site
comparison vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.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 | 4c8ae668cc8c |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
12:7a779792577d | 13:5fb285c0d0e3 |
---|---|
1 <?php | 1 <?php declare(strict_types=1); |
2 | 2 |
3 namespace PhpParser\NodeVisitor; | 3 namespace PhpParser\NodeVisitor; |
4 | 4 |
5 use PhpParser\Error; | 5 use PhpParser\Error; |
6 use PhpParser\ErrorHandler; | 6 use PhpParser\ErrorHandler; |
7 use PhpParser\NameContext; | |
7 use PhpParser\Node; | 8 use PhpParser\Node; |
8 use PhpParser\Node\Expr; | 9 use PhpParser\Node\Expr; |
9 use PhpParser\Node\Name; | 10 use PhpParser\Node\Name; |
10 use PhpParser\Node\Name\FullyQualified; | 11 use PhpParser\Node\Name\FullyQualified; |
11 use PhpParser\Node\Stmt; | 12 use PhpParser\Node\Stmt; |
12 use PhpParser\NodeVisitorAbstract; | 13 use PhpParser\NodeVisitorAbstract; |
13 | 14 |
14 class NameResolver extends NodeVisitorAbstract | 15 class NameResolver extends NodeVisitorAbstract |
15 { | 16 { |
16 /** @var null|Name Current namespace */ | 17 /** @var NameContext Naming context */ |
17 protected $namespace; | 18 protected $nameContext; |
18 | |
19 /** @var array Map of format [aliasType => [aliasName => originalName]] */ | |
20 protected $aliases; | |
21 | |
22 /** @var ErrorHandler Error handler */ | |
23 protected $errorHandler; | |
24 | 19 |
25 /** @var bool Whether to preserve original names */ | 20 /** @var bool Whether to preserve original names */ |
26 protected $preserveOriginalNames; | 21 protected $preserveOriginalNames; |
27 | 22 |
23 /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */ | |
24 protected $replaceNodes; | |
25 | |
28 /** | 26 /** |
29 * Constructs a name resolution visitor. | 27 * Constructs a name resolution visitor. |
30 * | 28 * |
31 * Options: If "preserveOriginalNames" is enabled, an "originalName" attribute will be added to | 29 * Options: |
32 * all name nodes that underwent resolution. | 30 * * preserveOriginalNames (default false): An "originalName" attribute will be added to |
31 * all name nodes that underwent resolution. | |
32 * * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a | |
33 * resolvedName attribute is added. (Names that cannot be statically resolved receive a | |
34 * namespacedName attribute, as usual.) | |
33 * | 35 * |
34 * @param ErrorHandler|null $errorHandler Error handler | 36 * @param ErrorHandler|null $errorHandler Error handler |
35 * @param array $options Options | 37 * @param array $options Options |
36 */ | 38 */ |
37 public function __construct(ErrorHandler $errorHandler = null, array $options = []) { | 39 public function __construct(ErrorHandler $errorHandler = null, array $options = []) { |
38 $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing; | 40 $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing); |
39 $this->preserveOriginalNames = !empty($options['preserveOriginalNames']); | 41 $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; |
42 $this->replaceNodes = $options['replaceNodes'] ?? true; | |
43 } | |
44 | |
45 /** | |
46 * Get name resolution context. | |
47 * | |
48 * @return NameContext | |
49 */ | |
50 public function getNameContext() : NameContext { | |
51 return $this->nameContext; | |
40 } | 52 } |
41 | 53 |
42 public function beforeTraverse(array $nodes) { | 54 public function beforeTraverse(array $nodes) { |
43 $this->resetState(); | 55 $this->nameContext->startNamespace(); |
56 return null; | |
44 } | 57 } |
45 | 58 |
46 public function enterNode(Node $node) { | 59 public function enterNode(Node $node) { |
47 if ($node instanceof Stmt\Namespace_) { | 60 if ($node instanceof Stmt\Namespace_) { |
48 $this->resetState($node->name); | 61 $this->nameContext->startNamespace($node->name); |
49 } elseif ($node instanceof Stmt\Use_) { | 62 } elseif ($node instanceof Stmt\Use_) { |
50 foreach ($node->uses as $use) { | 63 foreach ($node->uses as $use) { |
51 $this->addAlias($use, $node->type, null); | 64 $this->addAlias($use, $node->type, null); |
52 } | 65 } |
53 } elseif ($node instanceof Stmt\GroupUse) { | 66 } elseif ($node instanceof Stmt\GroupUse) { |
98 foreach ($node->types as &$type) { | 111 foreach ($node->types as &$type) { |
99 $type = $this->resolveClassName($type); | 112 $type = $this->resolveClassName($type); |
100 } | 113 } |
101 } elseif ($node instanceof Expr\FuncCall) { | 114 } elseif ($node instanceof Expr\FuncCall) { |
102 if ($node->name instanceof Name) { | 115 if ($node->name instanceof Name) { |
103 $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_FUNCTION); | 116 $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); |
104 } | 117 } |
105 } elseif ($node instanceof Expr\ConstFetch) { | 118 } elseif ($node instanceof Expr\ConstFetch) { |
106 $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_CONSTANT); | 119 $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); |
107 } elseif ($node instanceof Stmt\TraitUse) { | 120 } elseif ($node instanceof Stmt\TraitUse) { |
108 foreach ($node->traits as &$trait) { | 121 foreach ($node->traits as &$trait) { |
109 $trait = $this->resolveClassName($trait); | 122 $trait = $this->resolveClassName($trait); |
110 } | 123 } |
111 | 124 |
119 $insteadof = $this->resolveClassName($insteadof); | 132 $insteadof = $this->resolveClassName($insteadof); |
120 } | 133 } |
121 } | 134 } |
122 } | 135 } |
123 } | 136 } |
124 } | 137 |
125 | 138 return null; |
126 protected function resetState(Name $namespace = null) { | 139 } |
127 $this->namespace = $namespace; | 140 |
128 $this->aliases = array( | 141 private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) { |
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 | 142 // Add prefix for group uses |
137 $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; | 143 $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; |
138 // Type is determined either by individual element or whole use declaration | 144 // Type is determined either by individual element or whole use declaration |
139 $type |= $use->type; | 145 $type |= $use->type; |
140 | 146 |
141 // Constant names are case sensitive, everything else case insensitive | 147 $this->nameContext->addAlias( |
142 if ($type === Stmt\Use_::TYPE_CONSTANT) { | 148 $name, (string) $use->getAlias(), $type, $use->getAttributes() |
143 $aliasName = $use->alias; | 149 ); |
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 } | 150 } |
167 | 151 |
168 /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ | 152 /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ |
169 private function resolveSignature($node) { | 153 private function resolveSignature($node) { |
170 foreach ($node->params as $param) { | 154 foreach ($node->params as $param) { |
182 return $this->resolveClassName($node); | 166 return $this->resolveClassName($node); |
183 } | 167 } |
184 return $node; | 168 return $node; |
185 } | 169 } |
186 | 170 |
187 protected function resolveClassName(Name $name) { | 171 /** |
172 * Resolve name, according to name resolver options. | |
173 * | |
174 * @param Name $name Function or constant name to resolve | |
175 * @param int $type One of Stmt\Use_::TYPE_* | |
176 * | |
177 * @return Name Resolved name, or original name with attribute | |
178 */ | |
179 protected function resolveName(Name $name, int $type) : Name { | |
180 if (!$this->replaceNodes) { | |
181 $resolvedName = $this->nameContext->getResolvedName($name, $type); | |
182 if (null !== $resolvedName) { | |
183 $name->setAttribute('resolvedName', $resolvedName); | |
184 } else { | |
185 $name->setAttribute('namespacedName', FullyQualified::concat( | |
186 $this->nameContext->getNamespace(), $name, $name->getAttributes())); | |
187 } | |
188 return $name; | |
189 } | |
190 | |
188 if ($this->preserveOriginalNames) { | 191 if ($this->preserveOriginalNames) { |
189 // Save the original name | 192 // Save the original name |
190 $originalName = $name; | 193 $originalName = $name; |
191 $name = clone $originalName; | 194 $name = clone $originalName; |
192 $name->setAttribute('originalName', $originalName); | 195 $name->setAttribute('originalName', $originalName); |
193 } | 196 } |
194 | 197 |
195 // don't resolve special class names | 198 $resolvedName = $this->nameContext->getResolvedName($name, $type); |
196 if (in_array(strtolower($name->toString()), array('self', 'parent', 'static'))) { | 199 if (null !== $resolvedName) { |
197 if (!$name->isUnqualified()) { | 200 return $resolvedName; |
198 $this->errorHandler->handleError(new Error( | 201 } |
199 sprintf("'\\%s' is an invalid class name", $name->toString()), | 202 |
200 $name->getAttributes() | 203 // unqualified names inside a namespace cannot be resolved at compile-time |
201 )); | 204 // add the namespaced version of the name as an attribute |
202 } | 205 $name->setAttribute('namespacedName', FullyQualified::concat( |
203 return $name; | 206 $this->nameContext->getNamespace(), $name, $name->getAttributes())); |
204 } | 207 return $name; |
205 | 208 } |
206 // fully qualified names are already resolved | 209 |
207 if ($name->isFullyQualified()) { | 210 protected function resolveClassName(Name $name) { |
208 return $name; | 211 return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); |
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 } | 212 } |
268 | 213 |
269 protected function addNamespacedName(Node $node) { | 214 protected function addNamespacedName(Node $node) { |
270 $node->namespacedName = Name::concat($this->namespace, $node->name); | 215 $node->namespacedName = Name::concat( |
216 $this->nameContext->getNamespace(), (string) $node->name); | |
271 } | 217 } |
272 } | 218 } |