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