This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | /** |
||
6 | * This file is part of phpDocumentor. |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | * |
||
11 | * @link http://phpdoc.org |
||
12 | */ |
||
13 | |||
14 | namespace phpDocumentor\Reflection\Types; |
||
15 | |||
16 | use ArrayIterator; |
||
17 | use InvalidArgumentException; |
||
18 | use ReflectionClass; |
||
19 | use ReflectionClassConstant; |
||
20 | use ReflectionMethod; |
||
21 | use ReflectionParameter; |
||
22 | use ReflectionProperty; |
||
23 | use Reflector; |
||
24 | use RuntimeException; |
||
25 | use UnexpectedValueException; |
||
26 | use function file_exists; |
||
27 | use function file_get_contents; |
||
28 | use function get_class; |
||
29 | use function in_array; |
||
30 | use function is_string; |
||
31 | use function token_get_all; |
||
32 | use function trim; |
||
33 | use const T_AS; |
||
34 | use const T_CLASS; |
||
35 | use const T_CURLY_OPEN; |
||
36 | use const T_DOLLAR_OPEN_CURLY_BRACES; |
||
37 | use const T_NAMESPACE; |
||
38 | use const T_NS_SEPARATOR; |
||
39 | use const T_STRING; |
||
40 | use const T_USE; |
||
41 | |||
42 | /** |
||
43 | * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor. |
||
44 | * |
||
45 | * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to |
||
46 | * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to |
||
47 | * Fully Qualified names. |
||
48 | * |
||
49 | * @see Context for more information. |
||
50 | */ |
||
51 | final class ContextFactory |
||
52 | { |
||
53 | /** The literal used at the end of a use statement. */ |
||
54 | private const T_LITERAL_END_OF_USE = ';'; |
||
55 | |||
56 | /** The literal used between sets of use statements */ |
||
57 | private const T_LITERAL_USE_SEPARATOR = ','; |
||
58 | |||
59 | /** |
||
60 | * Build a Context given a Class Reflection. |
||
61 | * |
||
62 | * @see Context for more information on Contexts. |
||
63 | */ |
||
64 | 4 | public function createFromReflector(Reflector $reflector) : Context |
|
65 | { |
||
66 | 4 | if ($reflector instanceof ReflectionClass) { |
|
67 | 4 | //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable |
|
68 | /** @var ReflectionClass<object> $reflector */ |
||
69 | |||
70 | return $this->createFromReflectionClass($reflector); |
||
71 | } |
||
72 | |||
73 | if ($reflector instanceof ReflectionParameter) { |
||
74 | return $this->createFromReflectionParameter($reflector); |
||
75 | } |
||
76 | |||
77 | if ($reflector instanceof ReflectionMethod) { |
||
78 | return $this->createFromReflectionMethod($reflector); |
||
79 | } |
||
80 | |||
81 | if ($reflector instanceof ReflectionProperty) { |
||
82 | return $this->createFromReflectionProperty($reflector); |
||
83 | } |
||
84 | |||
85 | if ($reflector instanceof ReflectionClassConstant) { |
||
86 | return $this->createFromReflectionClassConstant($reflector); |
||
87 | } |
||
88 | |||
89 | throw new UnexpectedValueException('Unhandled \Reflector instance given: ' . get_class($reflector)); |
||
90 | } |
||
91 | |||
92 | private function createFromReflectionParameter(ReflectionParameter $parameter) : Context |
||
93 | { |
||
94 | $class = $parameter->getDeclaringClass(); |
||
95 | if (!$class) { |
||
96 | throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName()); |
||
97 | } |
||
98 | |||
99 | //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable |
||
100 | /** @var ReflectionClass<object> $class */ |
||
101 | |||
102 | return $this->createFromReflectionClass($class); |
||
103 | } |
||
104 | |||
105 | private function createFromReflectionMethod(ReflectionMethod $method) : Context |
||
106 | { |
||
107 | //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable |
||
108 | /** @var ReflectionClass<object> $class */ |
||
0 ignored issues
–
show
|
|||
109 | $class = $method->getDeclaringClass(); |
||
110 | |||
111 | return $this->createFromReflectionClass($class); |
||
112 | } |
||
113 | |||
114 | 4 | private function createFromReflectionProperty(ReflectionProperty $property) : Context |
|
115 | { |
||
116 | 4 | //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable |
|
117 | 4 | /** @var ReflectionClass<object> $class */ |
|
118 | $class = $property->getDeclaringClass(); |
||
119 | 4 | ||
120 | 2 | return $this->createFromReflectionClass($class); |
|
121 | 2 | } |
|
122 | |||
123 | private function createFromReflectionClassConstant(ReflectionClassConstant $constant) : Context |
||
124 | { |
||
125 | 2 | //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable |
|
126 | /** @var ReflectionClass<object> $class */ |
||
127 | $class = $constant->getDeclaringClass(); |
||
128 | 2 | ||
129 | return $this->createFromReflectionClass($class); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * @param ReflectionClass<object> $class |
||
0 ignored issues
–
show
The doc-type
ReflectionClass<object> could not be parsed: Expected "|" or "end of type", but got "<" at position 15. (view supported doc-types)
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.
Loading history...
|
|||
134 | */ |
||
135 | private function createFromReflectionClass(ReflectionClass $class) : Context |
||
136 | { |
||
137 | $fileName = $class->getFileName(); |
||
138 | $namespace = $class->getNamespaceName(); |
||
139 | |||
140 | 6 | if (is_string($fileName) && file_exists($fileName)) { |
|
141 | $contents = file_get_contents($fileName); |
||
142 | 6 | if ($contents === false) { |
|
143 | 6 | throw new RuntimeException('Unable to read file "' . $fileName . '"'); |
|
144 | 6 | } |
|
145 | 6 | ||
146 | return $this->createForNamespace($namespace, $contents); |
||
147 | 6 | } |
|
148 | 6 | ||
149 | 6 | return new Context($namespace, []); |
|
150 | 6 | } |
|
151 | 6 | ||
152 | 6 | /** |
|
153 | * Build a Context for a namespace in the provided file contents. |
||
154 | * |
||
155 | * @see Context for more information on Contexts. |
||
156 | 6 | * |
|
157 | 6 | * @param string $namespace It does not matter if a `\` precedes the namespace name, |
|
158 | 6 | * this method first normalizes. |
|
159 | 6 | * @param string $fileContents The file's contents to retrieve the aliases from with the given namespace. |
|
160 | 6 | */ |
|
161 | 6 | public function createForNamespace(string $namespace, string $fileContents) : Context |
|
162 | 6 | { |
|
163 | 6 | $namespace = trim($namespace, '\\'); |
|
164 | $useStatements = []; |
||
165 | $currentNamespace = ''; |
||
166 | 6 | $tokens = new ArrayIterator(token_get_all($fileContents)); |
|
167 | |||
168 | while ($tokens->valid()) { |
||
169 | 6 | $currentToken = $tokens->current(); |
|
170 | 6 | switch ($currentToken[0]) { |
|
171 | case T_NAMESPACE: |
||
172 | $currentNamespace = $this->parseNamespace($tokens); |
||
173 | 6 | break; |
|
174 | case T_CLASS: |
||
175 | 6 | // Fast-forward the iterator through the class so that any |
|
176 | 6 | // T_USE tokens found within are skipped - these are not |
|
177 | 4 | // valid namespace use statements so should be ignored. |
|
178 | 4 | $braceLevel = 0; |
|
179 | $firstBraceFound = false; |
||
180 | 4 | while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { |
|
181 | $currentToken = $tokens->current(); |
||
182 | if ($currentToken === '{' |
||
183 | 6 | || in_array($currentToken[0], [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true)) { |
|
184 | if (!$firstBraceFound) { |
||
185 | $firstBraceFound = true; |
||
186 | 6 | } |
|
187 | |||
188 | ++$braceLevel; |
||
189 | } |
||
190 | |||
191 | if ($currentToken === '}') { |
||
192 | 6 | --$braceLevel; |
|
193 | } |
||
194 | |||
195 | 6 | $tokens->next(); |
|
196 | } |
||
197 | 6 | ||
198 | 6 | break; |
|
199 | case T_USE: |
||
200 | 6 | if ($currentNamespace === $namespace) { |
|
201 | 6 | $useStatements += $this->parseUseStatement($tokens); |
|
202 | } |
||
203 | |||
204 | 6 | break; |
|
205 | } |
||
206 | |||
207 | $tokens->next(); |
||
208 | } |
||
209 | |||
210 | return new Context($namespace, $useStatements); |
||
211 | } |
||
212 | 4 | ||
213 | /** |
||
214 | 4 | * Deduce the name from tokens when we are at the T_NAMESPACE token. |
|
215 | * |
||
216 | 4 | * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens |
|
217 | 4 | */ |
|
218 | private function parseNamespace(ArrayIterator $tokens) : string |
||
219 | 4 | { |
|
220 | 4 | // skip to the first string or namespace separator |
|
221 | 4 | $this->skipToNextStringOrNamespaceSeparator($tokens); |
|
222 | |||
223 | $name = ''; |
||
224 | while ($tokens->valid() && in_array($tokens->current()[0], [T_STRING, T_NS_SEPARATOR], true)) { |
||
225 | $name .= $tokens->current()[1]; |
||
226 | $tokens->next(); |
||
227 | } |
||
228 | |||
229 | return $name; |
||
230 | } |
||
231 | 6 | ||
232 | /** |
||
233 | 6 | * Deduce the names of all imports when we are at the T_USE token. |
|
234 | 6 | * |
|
235 | * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens |
||
236 | 6 | * |
|
237 | * @return string[] |
||
238 | * |
||
239 | * @psalm-return array<string, string> |
||
240 | */ |
||
241 | private function parseUseStatement(ArrayIterator $tokens) : array |
||
242 | { |
||
243 | $uses = []; |
||
244 | |||
245 | while ($tokens->valid()) { |
||
246 | 4 | $this->skipToNextStringOrNamespaceSeparator($tokens); |
|
247 | |||
248 | 4 | $uses += $this->extractUseStatements($tokens); |
|
249 | 4 | $currentToken = $tokens->current(); |
|
250 | 4 | if ($currentToken[0] === self::T_LITERAL_END_OF_USE) { |
|
251 | 4 | return $uses; |
|
252 | 4 | } |
|
253 | } |
||
254 | 4 | ||
255 | 4 | return $uses; |
|
256 | 4 | } |
|
257 | 4 | ||
258 | /** |
||
259 | 4 | * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. |
|
260 | * |
||
261 | 4 | * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens |
|
262 | 4 | */ |
|
263 | 4 | private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens) : void |
|
264 | 4 | { |
|
265 | 4 | while ($tokens->valid()) { |
|
266 | 4 | $currentToken = $tokens->current(); |
|
267 | 4 | if (in_array($currentToken[0], [T_STRING, T_NS_SEPARATOR], true)) { |
|
268 | 4 | break; |
|
269 | 4 | } |
|
270 | 4 | ||
271 | 4 | $tokens->next(); |
|
272 | 4 | } |
|
273 | 4 | } |
|
274 | 4 | ||
275 | 4 | /** |
|
276 | 4 | * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of |
|
277 | 4 | * a USE statement yet. This will return a key/value array of the alias => namespace. |
|
278 | * |
||
279 | 4 | * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens |
|
280 | * |
||
281 | 4 | * @return string[] |
|
282 | 4 | * |
|
283 | * @psalm-suppress TypeDoesNotContainType |
||
284 | 4 | * |
|
285 | 4 | * @psalm-return array<string, string> |
|
286 | 4 | */ |
|
287 | 4 | private function extractUseStatements(ArrayIterator $tokens) : array |
|
288 | 4 | { |
|
289 | 4 | $extractedUseStatements = []; |
|
290 | 4 | $groupedNs = ''; |
|
291 | $currentNs = ''; |
||
292 | 4 | $currentAlias = ''; |
|
293 | $state = 'start'; |
||
294 | 4 | ||
295 | 4 | while ($tokens->valid()) { |
|
296 | $currentToken = $tokens->current(); |
||
297 | 4 | $tokenId = is_string($currentToken) ? $currentToken : $currentToken[0]; |
|
298 | 4 | $tokenValue = is_string($currentToken) ? null : $currentToken[1]; |
|
299 | 4 | switch ($state) { |
|
300 | 4 | case 'start': |
|
301 | 4 | switch ($tokenId) { |
|
302 | 4 | case T_STRING: |
|
303 | 4 | case T_NS_SEPARATOR: |
|
304 | 4 | $currentNs .= (string) $tokenValue; |
|
305 | 4 | $currentAlias = $tokenValue; |
|
306 | 4 | break; |
|
307 | 4 | case T_CURLY_OPEN: |
|
308 | 4 | case '{': |
|
309 | 4 | $state = 'grouped'; |
|
310 | 4 | $groupedNs = $currentNs; |
|
311 | 4 | break; |
|
312 | case T_AS: |
||
313 | $state = 'start-alias'; |
||
314 | break; |
||
315 | 4 | case self::T_LITERAL_USE_SEPARATOR: |
|
316 | case self::T_LITERAL_END_OF_USE: |
||
317 | 4 | $state = 'end'; |
|
318 | 4 | break; |
|
319 | default: |
||
320 | 4 | break; |
|
321 | 4 | } |
|
322 | 4 | ||
323 | 4 | break; |
|
324 | case 'start-alias': |
||
325 | switch ($tokenId) { |
||
326 | case T_STRING: |
||
327 | $currentAlias = $tokenValue; |
||
328 | break; |
||
329 | 4 | case self::T_LITERAL_USE_SEPARATOR: |
|
330 | 4 | case self::T_LITERAL_END_OF_USE: |
|
331 | 4 | $state = 'end'; |
|
332 | break; |
||
333 | 4 | default: |
|
334 | break; |
||
335 | } |
||
336 | |||
337 | 4 | break; |
|
338 | 4 | case 'grouped': |
|
339 | switch ($tokenId) { |
||
340 | case T_STRING: |
||
341 | 4 | case T_NS_SEPARATOR: |
|
342 | $currentNs .= (string) $tokenValue; |
||
343 | $currentAlias = $tokenValue; |
||
344 | 4 | break; |
|
345 | 4 | case T_AS: |
|
346 | $state = 'grouped-alias'; |
||
347 | break; |
||
348 | 4 | case self::T_LITERAL_USE_SEPARATOR: |
|
349 | $state = 'grouped'; |
||
350 | $extractedUseStatements[(string) $currentAlias] = $currentNs; |
||
351 | $currentNs = $groupedNs; |
||
352 | $currentAlias = ''; |
||
353 | break; |
||
354 | case self::T_LITERAL_END_OF_USE: |
||
355 | $state = 'end'; |
||
356 | break; |
||
357 | default: |
||
358 | break; |
||
359 | } |
||
360 | |||
361 | break; |
||
362 | case 'grouped-alias': |
||
363 | switch ($tokenId) { |
||
364 | case T_STRING: |
||
365 | $currentAlias = $tokenValue; |
||
366 | break; |
||
367 | case self::T_LITERAL_USE_SEPARATOR: |
||
368 | $state = 'grouped'; |
||
369 | $extractedUseStatements[(string) $currentAlias] = $currentNs; |
||
370 | $currentNs = $groupedNs; |
||
371 | $currentAlias = ''; |
||
372 | break; |
||
373 | case self::T_LITERAL_END_OF_USE: |
||
374 | $state = 'end'; |
||
375 | break; |
||
376 | default: |
||
377 | break; |
||
378 | } |
||
379 | } |
||
380 | |||
381 | if ($state === 'end') { |
||
382 | break; |
||
383 | } |
||
384 | |||
385 | $tokens->next(); |
||
386 | } |
||
387 | |||
388 | if ($groupedNs !== $currentNs) { |
||
389 | $extractedUseStatements[(string) $currentAlias] = $currentNs; |
||
390 | } |
||
391 | |||
392 | return $extractedUseStatements; |
||
393 | } |
||
394 | } |
||
395 |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.