Removed modules/contrib/media module to allow update to the core media module
[yaffs-website] / vendor / nikic / php-parser / lib / PhpParser / NameContext.php
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 }