PluginProbe
Elementor Website Builder – more than just a page builder / 3.25.1
Elementor Website Builder – more than just a page builder v3.25.1
4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 All 455 releases
elementor / vendor_prefixed / laravel / serializable-closure / src / Support / ReflectionClosure.php

ReflectionClosure.php in Elementor Website Builder – more than just a page builder 3.25.1, at vendor_prefixed/laravel/serializable-closure/src/Support/ReflectionClosure.php

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