PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / trunk
Matomo Analytics – Powerful, Privacy-First Insights for WordPress vtrunk
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / prefixed / opis / closure / src / ReflectionClosure.php
matomo / app / vendor / prefixed / opis / closure / src Last commit date
Analyzer.php 1 year ago ClosureContext.php 2 years ago ClosureScope.php 2 years ago ClosureStream.php 1 year ago ISecurityProvider.php 2 years ago ReflectionClosure.php 1 year ago SecurityException.php 2 years ago SecurityProvider.php 1 year ago SelfReference.php 2 years ago SerializableClosure.php 1 year ago
ReflectionClosure.php
999 lines
1 <?php
2
3 /* ===========================================================================
4 * Copyright (c) 2018-2021 Zindex Software
5 *
6 * Licensed under the MIT License
7 * =========================================================================== */
8 namespace Matomo\Dependencies\Opis\Closure;
9
10 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', -4);
11 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', -5);
12 defined('T_FN') || define('T_FN', -6);
13 use Closure;
14 use ReflectionFunction;
15 class ReflectionClosure extends ReflectionFunction
16 {
17 protected $code;
18 protected $tokens;
19 protected $hashedName;
20 protected $useVariables;
21 protected $isStaticClosure;
22 protected $isScopeRequired;
23 protected $isBindingRequired;
24 protected $isShortClosure;
25 protected static $files = array();
26 protected static $classes = array();
27 protected static $functions = array();
28 protected static $constants = array();
29 protected static $structures = array();
30 /**
31 * ReflectionClosure constructor.
32 * @param Closure $closure
33 * @param string|null $code This is ignored. Do not use it
34 * @throws \ReflectionException
35 */
36 public function __construct(Closure $closure, $code = null)
37 {
38 parent::__construct($closure);
39 }
40 /**
41 * @return bool
42 */
43 public function isStatic()
44 {
45 if ($this->isStaticClosure === null) {
46 $this->isStaticClosure = strtolower(substr($this->getCode(), 0, 6)) === 'static';
47 }
48 return $this->isStaticClosure;
49 }
50 public function isShortClosure()
51 {
52 if ($this->isShortClosure === null) {
53 $code = $this->getCode();
54 if ($this->isStatic()) {
55 $code = substr($code, 6);
56 }
57 $this->isShortClosure = strtolower(substr(trim($code), 0, 2)) === 'fn';
58 }
59 return $this->isShortClosure;
60 }
61 /**
62 * @return string
63 */
64 public function getCode()
65 {
66 if ($this->code !== null) {
67 return $this->code;
68 }
69 $fileName = $this->getFileName();
70 $line = $this->getStartLine() - 1;
71 $className = null;
72 if (null !== ($className = $this->getClosureScopeClass())) {
73 $className = '\\' . trim($className->getName(), '\\');
74 }
75 $builtin_types = self::getBuiltinTypes();
76 $class_keywords = ['self', 'static', 'parent'];
77 $ns = $this->getNamespaceName();
78 $nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\' . $ns);
79 $_file = var_export($fileName, \true);
80 $_dir = var_export(dirname($fileName), \true);
81 $_namespace = var_export($ns, \true);
82 $_class = var_export(trim($className, '\\'), \true);
83 $_function = $ns . ($ns == '' ? '' : '\\') . '{closure}';
84 $_method = ($className == '' ? '' : trim($className, '\\') . '::') . $_function;
85 $_function = var_export($_function, \true);
86 $_method = var_export($_method, \true);
87 $_trait = null;
88 $tokens = $this->getTokens();
89 $state = $lastState = 'start';
90 $inside_structure = \false;
91 $isShortClosure = \false;
92 $inside_structure_mark = 0;
93 $open = 0;
94 $code = '';
95 $id_start = $id_start_ci = $id_name = $context = '';
96 $classes = $functions = $constants = null;
97 $use = array();
98 $lineAdd = 0;
99 $isUsingScope = \false;
100 $isUsingThisObject = \false;
101 for ($i = 0, $l = count($tokens); $i < $l; $i++) {
102 $token = $tokens[$i];
103 switch ($state) {
104 case 'start':
105 if ($token[0] === \T_FUNCTION || $token[0] === \T_STATIC) {
106 $code .= $token[1];
107 $state = $token[0] === \T_FUNCTION ? 'function' : 'static';
108 } elseif ($token[0] === \T_FN) {
109 $isShortClosure = \true;
110 $code .= $token[1];
111 $state = 'closure_args';
112 }
113 break;
114 case 'static':
115 if ($token[0] === \T_WHITESPACE || $token[0] === \T_COMMENT || $token[0] === \T_FUNCTION) {
116 $code .= $token[1];
117 if ($token[0] === \T_FUNCTION) {
118 $state = 'function';
119 }
120 } elseif ($token[0] === \T_FN) {
121 $isShortClosure = \true;
122 $code .= $token[1];
123 $state = 'closure_args';
124 } else {
125 $code = '';
126 $state = 'start';
127 }
128 break;
129 case 'function':
130 switch ($token[0]) {
131 case \T_STRING:
132 $code = '';
133 $state = 'named_function';
134 break;
135 case '(':
136 $code .= '(';
137 $state = 'closure_args';
138 break;
139 default:
140 $code .= is_array($token) ? $token[1] : $token;
141 }
142 break;
143 case 'named_function':
144 if ($token[0] === \T_FUNCTION || $token[0] === \T_STATIC) {
145 $code = $token[1];
146 $state = $token[0] === \T_FUNCTION ? 'function' : 'static';
147 } elseif ($token[0] === \T_FN) {
148 $isShortClosure = \true;
149 $code .= $token[1];
150 $state = 'closure_args';
151 }
152 break;
153 case 'closure_args':
154 switch ($token[0]) {
155 case \T_NAME_QUALIFIED:
156 list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
157 $context = 'args';
158 $state = 'id_name';
159 $lastState = 'closure_args';
160 break;
161 case \T_NS_SEPARATOR:
162 case \T_STRING:
163 $id_start = $token[1];
164 $id_start_ci = strtolower($id_start);
165 $id_name = '';
166 $context = 'args';
167 $state = 'id_name';
168 $lastState = 'closure_args';
169 break;
170 case \T_USE:
171 $code .= $token[1];
172 $state = 'use';
173 break;
174 case \T_DOUBLE_ARROW:
175 $code .= $token[1];
176 if ($isShortClosure) {
177 $state = 'closure';
178 }
179 break;
180 case ':':
181 $code .= ':';
182 $state = 'return';
183 break;
184 case '{':
185 $code .= '{';
186 $state = 'closure';
187 $open++;
188 break;
189 default:
190 $code .= is_array($token) ? $token[1] : $token;
191 }
192 break;
193 case 'use':
194 switch ($token[0]) {
195 case \T_VARIABLE:
196 $use[] = substr($token[1], 1);
197 $code .= $token[1];
198 break;
199 case '{':
200 $code .= '{';
201 $state = 'closure';
202 $open++;
203 break;
204 case ':':
205 $code .= ':';
206 $state = 'return';
207 break;
208 default:
209 $code .= is_array($token) ? $token[1] : $token;
210 break;
211 }
212 break;
213 case 'return':
214 switch ($token[0]) {
215 case \T_WHITESPACE:
216 case \T_COMMENT:
217 case \T_DOC_COMMENT:
218 $code .= $token[1];
219 break;
220 case \T_NS_SEPARATOR:
221 case \T_STRING:
222 $id_start = $token[1];
223 $id_start_ci = strtolower($id_start);
224 $id_name = '';
225 $context = 'return_type';
226 $state = 'id_name';
227 $lastState = 'return';
228 break 2;
229 case \T_NAME_QUALIFIED:
230 list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
231 $context = 'return_type';
232 $state = 'id_name';
233 $lastState = 'return';
234 break 2;
235 case \T_DOUBLE_ARROW:
236 $code .= $token[1];
237 if ($isShortClosure) {
238 $state = 'closure';
239 }
240 break;
241 case '{':
242 $code .= '{';
243 $state = 'closure';
244 $open++;
245 break;
246 default:
247 $code .= is_array($token) ? $token[1] : $token;
248 break;
249 }
250 break;
251 case 'closure':
252 switch ($token[0]) {
253 case \T_CURLY_OPEN:
254 case \T_DOLLAR_OPEN_CURLY_BRACES:
255 case '{':
256 $code .= is_array($token) ? $token[1] : $token;
257 $open++;
258 break;
259 case '}':
260 $code .= '}';
261 if (--$open === 0 && !$isShortClosure) {
262 break 3;
263 } elseif ($inside_structure) {
264 $inside_structure = !($open === $inside_structure_mark);
265 }
266 break;
267 case '(':
268 case '[':
269 $code .= $token[0];
270 if ($isShortClosure) {
271 $open++;
272 }
273 break;
274 case ')':
275 case ']':
276 if ($isShortClosure) {
277 if ($open === 0) {
278 break 3;
279 }
280 --$open;
281 }
282 $code .= $token[0];
283 break;
284 case ',':
285 case ';':
286 if ($isShortClosure && $open === 0) {
287 break 3;
288 }
289 $code .= $token[0];
290 break;
291 case \T_LINE:
292 $code .= $token[2] - $line + $lineAdd;
293 break;
294 case \T_FILE:
295 $code .= $_file;
296 break;
297 case \T_DIR:
298 $code .= $_dir;
299 break;
300 case \T_NS_C:
301 $code .= $_namespace;
302 break;
303 case \T_CLASS_C:
304 $code .= $inside_structure ? $token[1] : $_class;
305 break;
306 case \T_FUNC_C:
307 $code .= $inside_structure ? $token[1] : $_function;
308 break;
309 case \T_METHOD_C:
310 $code .= $inside_structure ? $token[1] : $_method;
311 break;
312 case \T_COMMENT:
313 if (substr($token[1], 0, 8) === '#trackme') {
314 $timestamp = time();
315 $code .= '/**' . \PHP_EOL;
316 $code .= '* Date : ' . date(\DATE_W3C, $timestamp) . \PHP_EOL;
317 $code .= '* Timestamp : ' . $timestamp . \PHP_EOL;
318 $code .= '* Line : ' . ($line + 1) . \PHP_EOL;
319 $code .= '* File : ' . $_file . \PHP_EOL . '*/' . \PHP_EOL;
320 $lineAdd += 5;
321 } else {
322 $code .= $token[1];
323 }
324 break;
325 case \T_VARIABLE:
326 if ($token[1] == '$this' && !$inside_structure) {
327 $isUsingThisObject = \true;
328 }
329 $code .= $token[1];
330 break;
331 case \T_STATIC:
332 case \T_NS_SEPARATOR:
333 case \T_STRING:
334 $id_start = $token[1];
335 $id_start_ci = strtolower($id_start);
336 $id_name = '';
337 $context = 'root';
338 $state = 'id_name';
339 $lastState = 'closure';
340 break 2;
341 case \T_NAME_QUALIFIED:
342 list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
343 $context = 'root';
344 $state = 'id_name';
345 $lastState = 'closure';
346 break 2;
347 case \T_NEW:
348 $code .= $token[1];
349 $context = 'new';
350 $state = 'id_start';
351 $lastState = 'closure';
352 break 2;
353 case \T_USE:
354 $code .= $token[1];
355 $context = 'use';
356 $state = 'id_start';
357 $lastState = 'closure';
358 break;
359 case \T_INSTANCEOF:
360 case \T_INSTEADOF:
361 $code .= $token[1];
362 $context = 'instanceof';
363 $state = 'id_start';
364 $lastState = 'closure';
365 break;
366 case \T_OBJECT_OPERATOR:
367 case \T_DOUBLE_COLON:
368 $code .= $token[1];
369 $lastState = 'closure';
370 $state = 'ignore_next';
371 break;
372 case \T_FUNCTION:
373 $code .= $token[1];
374 $state = 'closure_args';
375 if (!$inside_structure) {
376 $inside_structure = \true;
377 $inside_structure_mark = $open;
378 }
379 break;
380 case \T_TRAIT_C:
381 if ($_trait === null) {
382 $startLine = $this->getStartLine();
383 $endLine = $this->getEndLine();
384 $structures = $this->getStructures();
385 $_trait = '';
386 foreach ($structures as &$struct) {
387 if ($struct['type'] === 'trait' && $struct['start'] <= $startLine && $struct['end'] >= $endLine) {
388 $_trait = ($ns == '' ? '' : $ns . '\\') . $struct['name'];
389 break;
390 }
391 }
392 $_trait = var_export($_trait, \true);
393 }
394 $code .= $_trait;
395 break;
396 default:
397 $code .= is_array($token) ? $token[1] : $token;
398 }
399 break;
400 case 'ignore_next':
401 switch ($token[0]) {
402 case \T_WHITESPACE:
403 case \T_COMMENT:
404 case \T_DOC_COMMENT:
405 $code .= $token[1];
406 break;
407 case \T_CLASS:
408 case \T_NEW:
409 case \T_STATIC:
410 case \T_VARIABLE:
411 case \T_STRING:
412 case \T_CLASS_C:
413 case \T_FILE:
414 case \T_DIR:
415 case \T_METHOD_C:
416 case \T_FUNC_C:
417 case \T_FUNCTION:
418 case \T_INSTANCEOF:
419 case \T_LINE:
420 case \T_NS_C:
421 case \T_TRAIT_C:
422 case \T_USE:
423 $code .= $token[1];
424 $state = $lastState;
425 break;
426 default:
427 $state = $lastState;
428 $i--;
429 }
430 break;
431 case 'id_start':
432 switch ($token[0]) {
433 case \T_WHITESPACE:
434 case \T_COMMENT:
435 case \T_DOC_COMMENT:
436 $code .= $token[1];
437 break;
438 case \T_NS_SEPARATOR:
439 case \T_NAME_FULLY_QUALIFIED:
440 case \T_STRING:
441 case \T_STATIC:
442 $id_start = $token[1];
443 $id_start_ci = strtolower($id_start);
444 $id_name = '';
445 $state = 'id_name';
446 break 2;
447 case \T_NAME_QUALIFIED:
448 list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
449 $state = 'id_name';
450 break 2;
451 case \T_VARIABLE:
452 $code .= $token[1];
453 $state = $lastState;
454 break;
455 case \T_CLASS:
456 $code .= $token[1];
457 $state = 'anonymous';
458 break;
459 default:
460 $i--;
461 //reprocess last
462 $state = 'id_name';
463 }
464 break;
465 case 'id_name':
466 switch ($token[0]) {
467 case \T_NAME_QUALIFIED:
468 case \T_NS_SEPARATOR:
469 case \T_STRING:
470 case \T_WHITESPACE:
471 case \T_COMMENT:
472 case \T_DOC_COMMENT:
473 $id_name .= $token[1];
474 break;
475 case '(':
476 if ($isShortClosure) {
477 $open++;
478 }
479 if ($context === 'new' || \false !== strpos($id_name, '\\')) {
480 if ($id_start_ci === 'self' || $id_start_ci === 'static') {
481 if (!$inside_structure) {
482 $isUsingScope = \true;
483 }
484 } elseif ($id_start !== '\\' && !in_array($id_start_ci, $class_keywords)) {
485 if ($classes === null) {
486 $classes = $this->getClasses();
487 }
488 if (isset($classes[$id_start_ci])) {
489 $id_start = $classes[$id_start_ci];
490 }
491 if ($id_start[0] !== '\\') {
492 $id_start = $nsf . '\\' . $id_start;
493 }
494 }
495 } else {
496 if ($id_start !== '\\') {
497 if ($functions === null) {
498 $functions = $this->getFunctions();
499 }
500 if (isset($functions[$id_start_ci])) {
501 $id_start = $functions[$id_start_ci];
502 } elseif ($nsf !== '\\' && function_exists($nsf . '\\' . $id_start)) {
503 $id_start = $nsf . '\\' . $id_start;
504 // Cache it to functions array
505 $functions[$id_start_ci] = $id_start;
506 }
507 }
508 }
509 $code .= $id_start . $id_name . '(';
510 $state = $lastState;
511 break;
512 case \T_VARIABLE:
513 case \T_DOUBLE_COLON:
514 if ($id_start !== '\\') {
515 if ($id_start_ci === 'self' || $id_start_ci === 'parent') {
516 if (!$inside_structure) {
517 $isUsingScope = \true;
518 }
519 } elseif ($id_start_ci === 'static') {
520 if (!$inside_structure) {
521 $isUsingScope = $token[0] === \T_DOUBLE_COLON;
522 }
523 } elseif (!(\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))) {
524 if ($classes === null) {
525 $classes = $this->getClasses();
526 }
527 if (isset($classes[$id_start_ci])) {
528 $id_start = $classes[$id_start_ci];
529 }
530 if ($id_start[0] !== '\\') {
531 $id_start = $nsf . '\\' . $id_start;
532 }
533 }
534 }
535 $code .= $id_start . $id_name . $token[1];
536 $state = $token[0] === \T_DOUBLE_COLON ? 'ignore_next' : $lastState;
537 break;
538 default:
539 if ($id_start !== '\\' && !defined($id_start)) {
540 if ($constants === null) {
541 $constants = $this->getConstants();
542 }
543 if (isset($constants[$id_start])) {
544 $id_start = $constants[$id_start];
545 } elseif ($context === 'new') {
546 if (in_array($id_start_ci, $class_keywords)) {
547 if (!$inside_structure) {
548 $isUsingScope = \true;
549 }
550 } else {
551 if ($classes === null) {
552 $classes = $this->getClasses();
553 }
554 if (isset($classes[$id_start_ci])) {
555 $id_start = $classes[$id_start_ci];
556 }
557 if ($id_start[0] !== '\\') {
558 $id_start = $nsf . '\\' . $id_start;
559 }
560 }
561 } elseif ($context === 'use' || $context === 'instanceof' || $context === 'args' || $context === 'return_type' || $context === 'extends' || $context === 'root') {
562 if (in_array($id_start_ci, $class_keywords)) {
563 if (!$inside_structure && !$id_start_ci === 'static') {
564 $isUsingScope = \true;
565 }
566 } elseif (!(\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))) {
567 if ($classes === null) {
568 $classes = $this->getClasses();
569 }
570 if (isset($classes[$id_start_ci])) {
571 $id_start = $classes[$id_start_ci];
572 }
573 if ($id_start[0] !== '\\') {
574 $id_start = $nsf . '\\' . $id_start;
575 }
576 }
577 }
578 }
579 $code .= $id_start . $id_name;
580 $state = $lastState;
581 $i--;
582 }
583 break;
584 case 'anonymous':
585 switch ($token[0]) {
586 case \T_NS_SEPARATOR:
587 case \T_STRING:
588 $id_start = $token[1];
589 $id_start_ci = strtolower($id_start);
590 $id_name = '';
591 $state = 'id_name';
592 $context = 'extends';
593 $lastState = 'anonymous';
594 break;
595 case '{':
596 $state = 'closure';
597 if (!$inside_structure) {
598 $inside_structure = \true;
599 $inside_structure_mark = $open;
600 }
601 $i--;
602 break;
603 default:
604 $code .= is_array($token) ? $token[1] : $token;
605 }
606 break;
607 }
608 }
609 if ($isShortClosure) {
610 $this->useVariables = $this->getStaticVariables();
611 } else {
612 $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
613 }
614 $this->isShortClosure = $isShortClosure;
615 $this->isBindingRequired = $isUsingThisObject;
616 $this->isScopeRequired = $isUsingScope;
617 $this->code = $code;
618 return $this->code;
619 }
620 /**
621 * @return array
622 */
623 private static function getBuiltinTypes()
624 {
625 // PHP 5
626 if (\PHP_MAJOR_VERSION === 5) {
627 return ['array', 'callable'];
628 }
629 // PHP 8
630 if (\PHP_MAJOR_VERSION === 8) {
631 return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null'];
632 }
633 // PHP 7
634 switch (\PHP_MINOR_VERSION) {
635 case 0:
636 return ['array', 'callable', 'string', 'int', 'bool', 'float'];
637 case 1:
638 return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void'];
639 default:
640 return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object'];
641 }
642 }
643 /**
644 * @return array
645 */
646 public function getUseVariables()
647 {
648 if ($this->useVariables !== null) {
649 return $this->useVariables;
650 }
651 $tokens = $this->getTokens();
652 $use = array();
653 $state = 'start';
654 foreach ($tokens as &$token) {
655 $is_array = is_array($token);
656 switch ($state) {
657 case 'start':
658 if ($is_array && $token[0] === \T_USE) {
659 $state = 'use';
660 }
661 break;
662 case 'use':
663 if ($is_array) {
664 if ($token[0] === \T_VARIABLE) {
665 $use[] = substr($token[1], 1);
666 }
667 } elseif ($token == ')') {
668 break 2;
669 }
670 break;
671 }
672 }
673 $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
674 return $this->useVariables;
675 }
676 /**
677 * return bool
678 */
679 public function isBindingRequired()
680 {
681 if ($this->isBindingRequired === null) {
682 $this->getCode();
683 }
684 return $this->isBindingRequired;
685 }
686 /**
687 * return bool
688 */
689 public function isScopeRequired()
690 {
691 if ($this->isScopeRequired === null) {
692 $this->getCode();
693 }
694 return $this->isScopeRequired;
695 }
696 /**
697 * @return string
698 */
699 protected function getHashedFileName()
700 {
701 if ($this->hashedName === null) {
702 $this->hashedName = sha1($this->getFileName());
703 }
704 return $this->hashedName;
705 }
706 /**
707 * @return array
708 */
709 protected function getFileTokens()
710 {
711 $key = $this->getHashedFileName();
712 if (!isset(static::$files[$key])) {
713 static::$files[$key] = token_get_all(file_get_contents($this->getFileName()));
714 }
715 return static::$files[$key];
716 }
717 /**
718 * @return array
719 */
720 protected function getTokens()
721 {
722 if ($this->tokens === null) {
723 $tokens = $this->getFileTokens();
724 $startLine = $this->getStartLine();
725 $endLine = $this->getEndLine();
726 $results = array();
727 $start = \false;
728 foreach ($tokens as &$token) {
729 if (!is_array($token)) {
730 if ($start) {
731 $results[] = $token;
732 }
733 continue;
734 }
735 $line = $token[2];
736 if ($line <= $endLine) {
737 if ($line >= $startLine) {
738 $start = \true;
739 $results[] = $token;
740 }
741 continue;
742 }
743 break;
744 }
745 $this->tokens = $results;
746 }
747 return $this->tokens;
748 }
749 /**
750 * @return array
751 */
752 protected function getClasses()
753 {
754 $key = $this->getHashedFileName();
755 if (!isset(static::$classes[$key])) {
756 $this->fetchItems();
757 }
758 return static::$classes[$key];
759 }
760 /**
761 * @return array
762 */
763 protected function getFunctions()
764 {
765 $key = $this->getHashedFileName();
766 if (!isset(static::$functions[$key])) {
767 $this->fetchItems();
768 }
769 return static::$functions[$key];
770 }
771 /**
772 * @return array
773 */
774 protected function getConstants()
775 {
776 $key = $this->getHashedFileName();
777 if (!isset(static::$constants[$key])) {
778 $this->fetchItems();
779 }
780 return static::$constants[$key];
781 }
782 /**
783 * @return array
784 */
785 protected function getStructures()
786 {
787 $key = $this->getHashedFileName();
788 if (!isset(static::$structures[$key])) {
789 $this->fetchItems();
790 }
791 return static::$structures[$key];
792 }
793 protected function fetchItems()
794 {
795 $key = $this->getHashedFileName();
796 $classes = array();
797 $functions = array();
798 $constants = array();
799 $structures = array();
800 $tokens = $this->getFileTokens();
801 $open = 0;
802 $state = 'start';
803 $lastState = '';
804 $prefix = '';
805 $name = '';
806 $alias = '';
807 $isFunc = $isConst = \false;
808 $startLine = $endLine = 0;
809 $structType = $structName = '';
810 $structIgnore = \false;
811 foreach ($tokens as $token) {
812 switch ($state) {
813 case 'start':
814 switch ($token[0]) {
815 case \T_CLASS:
816 case \T_INTERFACE:
817 case \T_TRAIT:
818 $state = 'before_structure';
819 $startLine = $token[2];
820 $structType = $token[0] == \T_CLASS ? 'class' : ($token[0] == \T_INTERFACE ? 'interface' : 'trait');
821 break;
822 case \T_USE:
823 $state = 'use';
824 $prefix = $name = $alias = '';
825 $isFunc = $isConst = \false;
826 break;
827 case \T_FUNCTION:
828 $state = 'structure';
829 $structIgnore = \true;
830 break;
831 case \T_NEW:
832 $state = 'new';
833 break;
834 case \T_OBJECT_OPERATOR:
835 case \T_DOUBLE_COLON:
836 $state = 'invoke';
837 break;
838 }
839 break;
840 case 'use':
841 switch ($token[0]) {
842 case \T_FUNCTION:
843 $isFunc = \true;
844 break;
845 case \T_CONST:
846 $isConst = \true;
847 break;
848 case \T_NS_SEPARATOR:
849 $name .= $token[1];
850 break;
851 case \T_STRING:
852 $name .= $token[1];
853 $alias = $token[1];
854 break;
855 case \T_NAME_QUALIFIED:
856 $name .= $token[1];
857 $pieces = explode('\\', $token[1]);
858 $alias = end($pieces);
859 break;
860 case \T_AS:
861 $lastState = 'use';
862 $state = 'alias';
863 break;
864 case '{':
865 $prefix = $name;
866 $name = $alias = '';
867 $state = 'use-group';
868 break;
869 case ',':
870 case ';':
871 if ($name === '' || $name[0] !== '\\') {
872 $name = '\\' . $name;
873 }
874 if ($alias !== '') {
875 if ($isFunc) {
876 $functions[strtolower($alias)] = $name;
877 } elseif ($isConst) {
878 $constants[$alias] = $name;
879 } else {
880 $classes[strtolower($alias)] = $name;
881 }
882 }
883 $name = $alias = '';
884 $state = $token === ';' ? 'start' : 'use';
885 break;
886 }
887 break;
888 case 'use-group':
889 switch ($token[0]) {
890 case \T_NS_SEPARATOR:
891 $name .= $token[1];
892 break;
893 case \T_NAME_QUALIFIED:
894 $name .= $token[1];
895 $pieces = explode('\\', $token[1]);
896 $alias = end($pieces);
897 break;
898 case \T_STRING:
899 $name .= $token[1];
900 $alias = $token[1];
901 break;
902 case \T_AS:
903 $lastState = 'use-group';
904 $state = 'alias';
905 break;
906 case ',':
907 case '}':
908 if ($prefix === '' || $prefix[0] !== '\\') {
909 $prefix = '\\' . $prefix;
910 }
911 if ($alias !== '') {
912 if ($isFunc) {
913 $functions[strtolower($alias)] = $prefix . $name;
914 } elseif ($isConst) {
915 $constants[$alias] = $prefix . $name;
916 } else {
917 $classes[strtolower($alias)] = $prefix . $name;
918 }
919 }
920 $name = $alias = '';
921 $state = $token === '}' ? 'use' : 'use-group';
922 break;
923 }
924 break;
925 case 'alias':
926 if ($token[0] === \T_STRING) {
927 $alias = $token[1];
928 $state = $lastState;
929 }
930 break;
931 case 'new':
932 switch ($token[0]) {
933 case \T_WHITESPACE:
934 case \T_COMMENT:
935 case \T_DOC_COMMENT:
936 break 2;
937 case \T_CLASS:
938 $state = 'structure';
939 $structIgnore = \true;
940 break;
941 default:
942 $state = 'start';
943 }
944 break;
945 case 'invoke':
946 switch ($token[0]) {
947 case \T_WHITESPACE:
948 case \T_COMMENT:
949 case \T_DOC_COMMENT:
950 break 2;
951 default:
952 $state = 'start';
953 }
954 break;
955 case 'before_structure':
956 if ($token[0] == \T_STRING) {
957 $structName = $token[1];
958 $state = 'structure';
959 }
960 break;
961 case 'structure':
962 switch ($token[0]) {
963 case '{':
964 case \T_CURLY_OPEN:
965 case \T_DOLLAR_OPEN_CURLY_BRACES:
966 $open++;
967 break;
968 case '}':
969 if (--$open == 0) {
970 if (!$structIgnore) {
971 $structures[] = array('type' => $structType, 'name' => $structName, 'start' => $startLine, 'end' => $endLine);
972 }
973 $structIgnore = \false;
974 $state = 'start';
975 }
976 break;
977 default:
978 if (is_array($token)) {
979 $endLine = $token[2];
980 }
981 }
982 break;
983 }
984 }
985 static::$classes[$key] = $classes;
986 static::$functions[$key] = $functions;
987 static::$constants[$key] = $constants;
988 static::$structures[$key] = $structures;
989 }
990 private function parseNameQualified($token)
991 {
992 $pieces = explode('\\', $token);
993 $id_start = array_shift($pieces);
994 $id_start_ci = strtolower($id_start);
995 $id_name = '\\' . implode('\\', $pieces);
996 return [$id_start, $id_start_ci, $id_name];
997 }
998 }
999