[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10 namespace Zend\Code\Scanner; 11 12 use ReflectionClass; 13 use Zend\Code\Annotation; 14 use Zend\Code\Exception; 15 use Zend\Code\NameInformation; 16 17 use function array_key_exists; 18 use function array_merge; 19 use function array_search; 20 use function array_slice; 21 use function array_values; 22 use function define; 23 use function defined; 24 use function explode; 25 use function in_array; 26 use function is_array; 27 use function is_int; 28 use function is_object; 29 use function is_string; 30 use function ltrim; 31 use function sprintf; 32 use function substr_count; 33 use function trigger_error; 34 35 class ClassScanner implements ScannerInterface 36 { 37 /** 38 * @var bool 39 */ 40 protected $isScanned = false; 41 42 /** 43 * @var string 44 */ 45 protected $docComment; 46 47 /** 48 * @var string 49 */ 50 protected $name; 51 52 /** 53 * @var string 54 */ 55 protected $shortName; 56 57 /** 58 * @var int 59 */ 60 protected $lineStart; 61 62 /** 63 * @var int 64 */ 65 protected $lineEnd; 66 67 /** 68 * @var bool 69 */ 70 protected $isTrait = false; 71 72 /** 73 * @var bool 74 */ 75 protected $isFinal = false; 76 77 /** 78 * @var bool 79 */ 80 protected $isAbstract = false; 81 82 /** 83 * @var bool 84 */ 85 protected $isInterface = false; 86 87 /** 88 * @var string 89 */ 90 protected $parentClass; 91 92 /** 93 * @var string 94 */ 95 protected $shortParentClass; 96 97 /** 98 * @var array 99 */ 100 protected $interfaces = []; 101 102 /** 103 * @var array 104 */ 105 protected $shortInterfaces = []; 106 107 /** 108 * @var array 109 */ 110 protected $tokens = []; 111 112 /** 113 * @var NameInformation 114 */ 115 protected $nameInformation; 116 117 /** 118 * @var array 119 */ 120 protected $infos = []; 121 122 /** 123 * @var array 124 */ 125 protected $traits = []; 126 127 /** 128 * @var array 129 */ 130 protected $methods = []; 131 132 /** 133 * @param array $classTokens 134 * @param NameInformation|null $nameInformation 135 * @return ClassScanner 136 */ 137 public function __construct(array $classTokens, NameInformation $nameInformation = null) 138 { 139 $this->tokens = $classTokens; 140 $this->nameInformation = $nameInformation; 141 } 142 143 /** 144 * Get annotations 145 * 146 * @param Annotation\AnnotationManager $annotationManager 147 * @return Annotation\AnnotationCollection 148 */ 149 public function getAnnotations(Annotation\AnnotationManager $annotationManager) 150 { 151 if (($docComment = $this->getDocComment()) == '') { 152 return false; 153 } 154 155 return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation); 156 } 157 158 /** 159 * Return documentation comment 160 * 161 * @return null|string 162 */ 163 public function getDocComment() 164 { 165 $this->scan(); 166 167 return $this->docComment; 168 } 169 170 /** 171 * Return documentation block 172 * 173 * @return false|DocBlockScanner 174 */ 175 public function getDocBlock() 176 { 177 if (! $docComment = $this->getDocComment()) { 178 return false; 179 } 180 181 return new DocBlockScanner($docComment); 182 } 183 184 /** 185 * Return a name of class 186 * 187 * @return null|string 188 */ 189 public function getName() 190 { 191 $this->scan(); 192 return $this->name; 193 } 194 195 /** 196 * Return short name of class 197 * 198 * @return null|string 199 */ 200 public function getShortName() 201 { 202 $this->scan(); 203 return $this->shortName; 204 } 205 206 /** 207 * Return number of first line 208 * 209 * @return int|null 210 */ 211 public function getLineStart() 212 { 213 $this->scan(); 214 return $this->lineStart; 215 } 216 217 /** 218 * Return number of last line 219 * 220 * @return int|null 221 */ 222 public function getLineEnd() 223 { 224 $this->scan(); 225 return $this->lineEnd; 226 } 227 228 /** 229 * Verify if class is final 230 * 231 * @return bool 232 */ 233 public function isFinal() 234 { 235 $this->scan(); 236 return $this->isFinal; 237 } 238 239 /** 240 * Verify if class is a trait 241 * 242 * @return bool 243 */ 244 public function isTrait() 245 { 246 $this->scan(); 247 return $this->isTrait; 248 } 249 250 /** 251 * Verify if class is instantiable 252 * 253 * @return bool 254 */ 255 public function isInstantiable() 256 { 257 $this->scan(); 258 return ! $this->isAbstract && ! $this->isInterface && ! $this->isTrait; 259 } 260 261 /** 262 * Verify if class is an abstract class 263 * 264 * @return bool 265 */ 266 public function isAbstract() 267 { 268 $this->scan(); 269 return $this->isAbstract; 270 } 271 272 /** 273 * Verify if class is an interface 274 * 275 * @return bool 276 */ 277 public function isInterface() 278 { 279 $this->scan(); 280 return $this->isInterface; 281 } 282 283 /** 284 * Verify if class has parent 285 * 286 * @return bool 287 */ 288 public function hasParentClass() 289 { 290 $this->scan(); 291 return $this->parentClass !== null; 292 } 293 294 /** 295 * Return a name of parent class 296 * 297 * @return null|string 298 */ 299 public function getParentClass() 300 { 301 $this->scan(); 302 return $this->parentClass; 303 } 304 305 /** 306 * Return a list of interface names 307 * 308 * @return array 309 */ 310 public function getInterfaces() 311 { 312 $this->scan(); 313 return $this->interfaces; 314 } 315 316 /** 317 * Return a list of constant names 318 * 319 * @return array 320 */ 321 public function getConstantNames() 322 { 323 $this->scan(); 324 325 $return = []; 326 foreach ($this->infos as $info) { 327 if ($info['type'] != 'constant') { 328 continue; 329 } 330 331 $return[] = $info['name']; 332 } 333 334 return $return; 335 } 336 337 /** 338 * Return a list of constants 339 * 340 * @param bool $namesOnly Set false to return instances of ConstantScanner 341 * @return array|ConstantScanner[] 342 */ 343 public function getConstants($namesOnly = true) 344 { 345 if (true === $namesOnly) { 346 trigger_error('Use method getConstantNames() instead', E_USER_DEPRECATED); 347 return $this->getConstantNames(); 348 } 349 350 $this->scan(); 351 352 $return = []; 353 foreach ($this->infos as $info) { 354 if ($info['type'] != 'constant') { 355 continue; 356 } 357 358 $return[] = $this->getConstant($info['name']); 359 } 360 361 return $return; 362 } 363 364 /** 365 * Return a single constant by given name or index of info 366 * 367 * @param string|int $constantNameOrInfoIndex 368 * @throws Exception\InvalidArgumentException 369 * @return bool|ConstantScanner 370 */ 371 public function getConstant($constantNameOrInfoIndex) 372 { 373 $this->scan(); 374 375 if (is_int($constantNameOrInfoIndex)) { 376 $info = $this->infos[$constantNameOrInfoIndex]; 377 if ($info['type'] != 'constant') { 378 throw new Exception\InvalidArgumentException('Index of info offset is not about a constant'); 379 } 380 } elseif (is_string($constantNameOrInfoIndex)) { 381 $constantFound = false; 382 foreach ($this->infos as $info) { 383 if ($info['type'] === 'constant' && $info['name'] === $constantNameOrInfoIndex) { 384 $constantFound = true; 385 break; 386 } 387 } 388 if (! $constantFound) { 389 return false; 390 } 391 } else { 392 throw new Exception\InvalidArgumentException( 393 'Invalid constant name of info index type. Must be of type int or string' 394 ); 395 } 396 if (! isset($info)) { 397 return false; 398 } 399 $p = new ConstantScanner( 400 array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1), 401 $this->nameInformation 402 ); 403 $p->setClass($this->name); 404 $p->setScannerClass($this); 405 return $p; 406 } 407 408 /** 409 * Verify if class has constant 410 * 411 * @param string $name 412 * @return bool 413 */ 414 public function hasConstant($name) 415 { 416 $this->scan(); 417 418 foreach ($this->infos as $info) { 419 if ($info['type'] === 'constant' && $info['name'] === $name) { 420 return true; 421 } 422 } 423 424 return false; 425 } 426 427 /** 428 * Return a list of property names 429 * 430 * @return array 431 */ 432 public function getPropertyNames() 433 { 434 $this->scan(); 435 436 $return = []; 437 foreach ($this->infos as $info) { 438 if ($info['type'] != 'property') { 439 continue; 440 } 441 442 $return[] = $info['name']; 443 } 444 445 return $return; 446 } 447 448 /** 449 * Return a list of properties 450 * 451 * @return PropertyScanner[] 452 */ 453 public function getProperties() 454 { 455 $this->scan(); 456 457 $return = []; 458 foreach ($this->infos as $info) { 459 if ($info['type'] != 'property') { 460 continue; 461 } 462 463 $return[] = $this->getProperty($info['name']); 464 } 465 466 return $return; 467 } 468 469 /** 470 * Return a single property by given name or index of info 471 * 472 * @param string|int $propertyNameOrInfoIndex 473 * @throws Exception\InvalidArgumentException 474 * @return bool|PropertyScanner 475 */ 476 public function getProperty($propertyNameOrInfoIndex) 477 { 478 $this->scan(); 479 480 if (is_int($propertyNameOrInfoIndex)) { 481 $info = $this->infos[$propertyNameOrInfoIndex]; 482 if ($info['type'] != 'property') { 483 throw new Exception\InvalidArgumentException('Index of info offset is not about a property'); 484 } 485 } elseif (is_string($propertyNameOrInfoIndex)) { 486 $propertyFound = false; 487 foreach ($this->infos as $info) { 488 if ($info['type'] === 'property' && $info['name'] === $propertyNameOrInfoIndex) { 489 $propertyFound = true; 490 break; 491 } 492 } 493 if (! $propertyFound) { 494 return false; 495 } 496 } else { 497 throw new Exception\InvalidArgumentException( 498 'Invalid property name of info index type. Must be of type int or string' 499 ); 500 } 501 if (! isset($info)) { 502 return false; 503 } 504 $p = new PropertyScanner( 505 array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1), 506 $this->nameInformation 507 ); 508 $p->setClass($this->name); 509 $p->setScannerClass($this); 510 return $p; 511 } 512 513 /** 514 * Verify if class has property 515 * 516 * @param string $name 517 * @return bool 518 */ 519 public function hasProperty($name) 520 { 521 $this->scan(); 522 523 foreach ($this->infos as $info) { 524 if ($info['type'] === 'property' && $info['name'] === $name) { 525 return true; 526 } 527 } 528 529 return false; 530 } 531 532 /** 533 * Retrieve any traits used by the class. 534 * 535 * @return ClassScanner[] 536 */ 537 public function getTraits() 538 { 539 if (! empty($this->traits)) { 540 return $this->traits; 541 } 542 543 // get list of trait names 544 $traitNames = $this->getTraitNames(); 545 foreach ($traitNames as $traitName) { 546 $r = new ReflectionClass($traitName); 547 if (! $r->isTrait()) { 548 throw new Exception\RuntimeException(sprintf( 549 'Non-trait class detected as a trait: %s', 550 $traitName 551 )); 552 } 553 $fileName = $r->getFileName(); 554 555 $file = new FileScanner($fileName); 556 $this->traits[] = $file->getClass($traitName); 557 } 558 559 return $this->traits; 560 } 561 562 /** 563 * Retrieve a list of trait names used by this class. 564 * 565 * @return array 566 */ 567 public function getTraitNames() 568 { 569 $this->scan(); 570 571 $return = []; 572 foreach ($this->infos as $info) { 573 if ($info['type'] !== 'use') { 574 continue; 575 } 576 577 if (is_array($info['use_statements'])) { 578 foreach ($info['use_statements'] as $trait) { 579 $traitName = $trait; 580 if ($this->nameInformation instanceof NameInformation) { 581 $traitName = $this->nameInformation->resolveName($traitName); 582 } 583 $return[] = $traitName; 584 } 585 } 586 } 587 588 return $return; 589 } 590 591 /** 592 * Retrieve a list of aliased traits used by the class. 593 * 594 * @return array 595 */ 596 public function getTraitAliases() 597 { 598 $this->scan(); 599 600 $return = []; 601 foreach ($this->infos as $info) { 602 if ($info['type'] !== 'use') { 603 continue; 604 } 605 606 if (is_array($info['aliases'])) { 607 foreach ($info['aliases'] as $alias) { 608 if (null === $alias 609 || (! empty($alias['type']) && $alias['type'] !== 'as') 610 ) { 611 continue; 612 } 613 614 // attempt to get fqcn 615 list($trait, $method) = explode('::', $alias['original']); 616 if ($this->nameInformation instanceof NameInformation) { 617 $trait = $this->nameInformation->resolveName($trait); 618 } 619 620 $return[$alias['alias']] = $trait . '::' . $method; 621 } 622 } 623 } 624 625 return $return; 626 } 627 628 /** 629 * Retrieve visibility for a given alias. 630 * 631 * @param mixed $aliasName 632 * @return string 633 */ 634 protected function getVisibilityForAlias($aliasName) 635 { 636 $this->scan(); 637 638 $return = null; 639 foreach ($this->infos as $info) { 640 if ($info['type'] !== 'use') { 641 continue; 642 } 643 644 if (is_array($info['aliases'])) { 645 foreach ($info['aliases'] as $alias) { 646 if (null === $alias 647 && (! empty($alias['type']) && $alias['type'] !== 'as') 648 ) { 649 continue; 650 } 651 652 if ($alias['alias'] === $aliasName) { 653 $return = $alias['visibility']; 654 break 2; 655 } 656 } 657 } 658 } 659 660 return $return; 661 } 662 663 /** 664 * Return an array of key = trait to keep, value = trait::method to ignore 665 * 666 * @return array 667 */ 668 protected function getBlockedTraitMethods() 669 { 670 $this->scan(); 671 672 $return = []; 673 foreach ($this->infos as $info) { 674 if ($info['type'] !== 'use') { 675 continue; 676 } 677 678 if (is_array($info['aliases'])) { 679 foreach ($info['aliases'] as $alias) { 680 if (null === $alias 681 || (! empty($alias['type']) && $alias['type'] !== 'insteadof') 682 ) { 683 continue; 684 } 685 686 // attempt to get fqcn 687 list($trait, $method) = explode('::', $alias['original']); 688 if ($this->nameInformation instanceof NameInformation) { 689 $trait = $this->nameInformation->resolveName($alias['alias']); 690 } 691 692 $return[] = $trait . '::' . $method; 693 } 694 } 695 } 696 697 return $return; 698 } 699 700 /** 701 * Return a list of method names 702 * 703 * @return array 704 */ 705 public function getMethodNames() 706 { 707 $this->scan(); 708 709 $methods = $this->getMethods(); 710 $return = []; 711 foreach ($methods as $method) { 712 $return[] = $method->getName(); 713 } 714 715 return $return; 716 } 717 718 /** 719 * Return a list of methods 720 * 721 * @return MethodScanner[] 722 */ 723 public function getMethods() 724 { 725 $this->scan(); 726 727 if (! empty($this->methods)) { 728 return $this->methods; 729 } 730 731 foreach ($this->infos as $info) { 732 if ($info['type'] !== 'method' && $info['type'] !== 'use') { 733 continue; 734 } 735 736 // Merge in trait methods 737 if ($info['type'] === 'use') { 738 $traitMethods = []; 739 $traits = $this->getTraits(); 740 $insteadof = $this->getBlockedTraitMethods(); 741 $aliases = $this->getTraitAliases(); 742 743 foreach ($traits as $trait) { 744 $tempMethods = $trait->getMethods(); 745 foreach ($tempMethods as $tempMethod) { 746 $methodFullName = $trait->getName() . '::' . $tempMethod->getName(); 747 $methodAlias = array_search($methodFullName, $aliases); 748 749 if (false !== $methodAlias) { 750 // trait::method is aliased 751 // clone the tempMethod as we need to change 752 // the name and possibly the visibility of the 753 // scanned method. 754 // 755 // @todo setName and setVisibility were added to 756 // MethodScanner to accomplish this, may not be the 757 // best option, could use ReflectionClass instead? 758 $newMethod = clone $tempMethod; 759 $newMethod->setName($methodAlias); 760 761 // if visibility exists, change it on the MethodScanner 762 $visibility = $this->getVisibilityForAlias($methodAlias); 763 if (null !== $visibility) { 764 $newMethod->setVisibility($visibility); 765 } 766 $traitMethods[$methodAlias] = $newMethod; 767 } elseif (in_array($methodFullName, $insteadof)) { 768 // ignore overridden methods 769 continue; 770 } else { 771 if (array_key_exists($tempMethod->getName(), $traitMethods)) { 772 throw new Exception\RuntimeException(sprintf( 773 'Trait method %s has not been applied because there are' 774 . ' collisions with other trait methods see: (insteadof OR as)', 775 $tempMethod->getName() 776 )); 777 } 778 779 $traitMethods[$tempMethod->getName()] = $tempMethod; 780 } 781 } 782 } 783 784 $this->methods = array_merge($this->methods, array_values($traitMethods)); 785 continue; 786 } 787 788 $m = new MethodScanner( 789 array_slice( 790 $this->tokens, 791 $info['tokenStart'], 792 $info['tokenEnd'] - $info['tokenStart'] + 1 793 ), 794 $this->nameInformation 795 ); 796 $m->setClass($this->name); 797 $m->setScannerClass($this); 798 799 $this->methods[] = $m; 800 } 801 802 return $this->methods; 803 } 804 805 /** 806 * Return a single method by given name or index of info 807 * 808 * @param string|int $methodNameOrInfoIndex 809 * @throws Exception\InvalidArgumentException 810 * @return MethodScanner 811 */ 812 public function getMethod($methodNameOrInfoIndex) 813 { 814 $this->scan(); 815 816 if (is_int($methodNameOrInfoIndex)) { 817 $info = $this->infos[$methodNameOrInfoIndex]; 818 if ($info['type'] != 'method') { 819 throw new Exception\InvalidArgumentException('Index of info offset is not about a method'); 820 } 821 $methodNameOrInfoIndex = $info['name']; 822 } 823 824 $returnMethod = false; 825 $methods = $this->getMethods(); 826 foreach ($methods as $method) { 827 if ($method->getName() === $methodNameOrInfoIndex) { 828 $returnMethod = $method; 829 break; 830 } 831 } 832 833 return $returnMethod; 834 } 835 836 /** 837 * Verify if class has method by given name 838 * 839 * @param string $name 840 * @return bool 841 */ 842 public function hasMethod($name) 843 { 844 $this->scan(); 845 846 return is_object($this->getMethod($name)); 847 } 848 849 public static function export() 850 { 851 // @todo 852 } 853 854 public function __toString() 855 { 856 // @todo 857 } 858 859 /** 860 * Scan tokens 861 * 862 * @return void 863 * @throws Exception\RuntimeException 864 */ 865 protected function scan() 866 { 867 if ($this->isScanned) { 868 return; 869 } 870 871 if (! $this->tokens) { 872 throw new Exception\RuntimeException('No tokens were provided'); 873 } 874 875 /** 876 * Variables & Setup 877 */ 878 $tokens = &$this->tokens; // localize 879 $infos = &$this->infos; // localize 880 $tokenIndex = null; 881 $token = null; 882 $tokenType = null; 883 $tokenContent = null; 884 $tokenLine = null; 885 $namespace = null; 886 $infoIndex = 0; 887 $braceCount = 0; 888 889 /* 890 * MACRO creation 891 */ 892 $MACRO_TOKEN_ADVANCE = function () use ( 893 &$tokens, 894 &$tokenIndex, 895 &$token, 896 &$tokenType, 897 &$tokenContent, 898 &$tokenLine 899 ) { 900 static $lastTokenArray = null; 901 $tokenIndex = $tokenIndex === null ? 0 : $tokenIndex + 1; 902 if (! isset($tokens[$tokenIndex])) { 903 $token = false; 904 $tokenContent = false; 905 $tokenType = false; 906 $tokenLine = false; 907 908 return false; 909 } 910 $token = $tokens[$tokenIndex]; 911 912 if (is_string($token)) { 913 $tokenType = null; 914 $tokenContent = $token; 915 $tokenLine += substr_count( 916 $lastTokenArray[1] ?? '', 917 "\n" 918 ); // adjust token line by last known newline count 919 } else { 920 $lastTokenArray = $token; 921 [$tokenType, $tokenContent, $tokenLine] = $token; 922 } 923 924 return $tokenIndex; 925 }; 926 $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) { 927 $infos[$infoIndex]['tokenEnd'] = $tokenIndex; 928 $infos[$infoIndex]['lineEnd'] = $tokenLine; 929 $infoIndex++; 930 931 return $infoIndex; 932 }; 933 934 /** 935 * START FINITE STATE MACHINE FOR SCANNING TOKENS 936 */ 937 // Initialize token 938 $MACRO_TOKEN_ADVANCE(); 939 940 SCANNER_TOP: 941 942 switch ($tokenType) { 943 case T_DOC_COMMENT: 944 $this->docComment = $tokenContent; 945 goto SCANNER_CONTINUE; 946 //goto no break needed 947 948 case T_FINAL: 949 case T_ABSTRACT: 950 case T_CLASS: 951 case T_INTERFACE: 952 case T_TRAIT: 953 // CLASS INFORMATION 954 955 $classContext = null; 956 $classInterfaceIndex = 0; 957 958 SCANNER_CLASS_INFO_TOP: 959 960 if (is_string($tokens[$tokenIndex + 1]) && $tokens[$tokenIndex + 1] === '{') { 961 goto SCANNER_CLASS_INFO_END; 962 } 963 964 $this->lineStart = $tokenLine; 965 966 switch ($tokenType) { 967 case T_FINAL: 968 $this->isFinal = true; 969 goto SCANNER_CLASS_INFO_CONTINUE; 970 // goto no break needed 971 972 case T_ABSTRACT: 973 $this->isAbstract = true; 974 goto SCANNER_CLASS_INFO_CONTINUE; 975 // goto no break needed 976 977 case T_TRAIT: 978 $this->isTrait = true; 979 $this->shortName = $tokens[$tokenIndex + 2][1]; 980 if ($this->nameInformation && $this->nameInformation->hasNamespace()) { 981 $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName; 982 } else { 983 $this->name = $this->shortName; 984 } 985 goto SCANNER_CLASS_INFO_CONTINUE; 986 // goto no break needed 987 988 case T_INTERFACE: 989 $this->isInterface = true; 990 // fall-through 991 case T_CLASS: 992 $this->shortName = $tokens[$tokenIndex + 2][1]; 993 if ($this->nameInformation && $this->nameInformation->hasNamespace()) { 994 $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName; 995 } else { 996 $this->name = $this->shortName; 997 } 998 goto SCANNER_CLASS_INFO_CONTINUE; 999 // goto no break needed 1000 1001 case T_NS_SEPARATOR: 1002 case T_STRING: 1003 switch ($classContext) { 1004 case T_EXTENDS: 1005 if ($this->isInterface) { 1006 $this->shortInterfaces[$classInterfaceIndex] .= $tokenContent; 1007 } else { 1008 $this->shortParentClass .= $tokenContent; 1009 } 1010 break; 1011 case T_IMPLEMENTS: 1012 $this->shortInterfaces[$classInterfaceIndex] .= $tokenContent; 1013 break; 1014 } 1015 goto SCANNER_CLASS_INFO_CONTINUE; 1016 // goto no break needed 1017 1018 case T_EXTENDS: 1019 case T_IMPLEMENTS: 1020 $classContext = $tokenType; 1021 if (($this->isInterface && $classContext === T_EXTENDS) || $classContext === T_IMPLEMENTS) { 1022 $this->shortInterfaces[$classInterfaceIndex] = ''; 1023 } elseif (! $this->isInterface && $classContext === T_EXTENDS) { 1024 $this->shortParentClass = ''; 1025 } 1026 goto SCANNER_CLASS_INFO_CONTINUE; 1027 // goto no break needed 1028 1029 case null: 1030 if (($classContext == T_IMPLEMENTS && $tokenContent == ',') 1031 || ($classContext == T_EXTENDS && $tokenContent == ',' && $this->isInterface) 1032 ) { 1033 $classInterfaceIndex++; 1034 $this->shortInterfaces[$classInterfaceIndex] = ''; 1035 } 1036 } 1037 1038 SCANNER_CLASS_INFO_CONTINUE: 1039 1040 if ($MACRO_TOKEN_ADVANCE() === false) { 1041 goto SCANNER_END; 1042 } 1043 goto SCANNER_CLASS_INFO_TOP; 1044 1045 SCANNER_CLASS_INFO_END: 1046 1047 goto SCANNER_CONTINUE; 1048 } 1049 1050 if ($tokenType === null && $tokenContent === '{' && $braceCount === 0) { 1051 $braceCount++; 1052 if ($MACRO_TOKEN_ADVANCE() === false) { 1053 goto SCANNER_END; 1054 } 1055 1056 SCANNER_CLASS_BODY_TOP: 1057 1058 if ($braceCount === 0) { 1059 goto SCANNER_CLASS_BODY_END; 1060 } 1061 1062 switch ($tokenType) { 1063 case T_CONST: 1064 $infos[$infoIndex] = [ 1065 'type' => 'constant', 1066 'tokenStart' => $tokenIndex, 1067 'tokenEnd' => null, 1068 'lineStart' => $tokenLine, 1069 'lineEnd' => null, 1070 'name' => null, 1071 'value' => null, 1072 ]; 1073 1074 SCANNER_CLASS_BODY_CONST_TOP: 1075 1076 if ($tokenContent === ';') { 1077 goto SCANNER_CLASS_BODY_CONST_END; 1078 } 1079 1080 if ($tokenType === T_STRING && null === $infos[$infoIndex]['name']) { 1081 $infos[$infoIndex]['name'] = $tokenContent; 1082 } 1083 1084 SCANNER_CLASS_BODY_CONST_CONTINUE: 1085 1086 if ($MACRO_TOKEN_ADVANCE() === false) { 1087 goto SCANNER_END; 1088 } 1089 goto SCANNER_CLASS_BODY_CONST_TOP; 1090 1091 SCANNER_CLASS_BODY_CONST_END: 1092 1093 $MACRO_INFO_ADVANCE(); 1094 goto SCANNER_CLASS_BODY_CONTINUE; 1095 // goto no break needed 1096 1097 case T_USE: 1098 // ensure php backwards compatibility 1099 if (! defined('T_INSTEADOF')) { 1100 define('T_INSTEADOF', 24000); 1101 } 1102 1103 $infos[$infoIndex] = [ 1104 'type' => 'use', 1105 'tokenStart' => $tokenIndex, 1106 'tokenEnd' => null, 1107 'lineStart' => $tokens[$tokenIndex][2], 1108 'lineEnd' => null, 1109 'name' => $namespace, 1110 'use_statements' => [0 => null], 1111 'aliases' => [0 => null], 1112 ]; 1113 1114 $isOriginalName = [T_STRING, T_DOUBLE_COLON]; 1115 $isAlias = [T_STRING]; 1116 $isVisibility = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STATIC]; 1117 $isAliasType = [T_AS, T_INSTEADOF]; 1118 $isValidAlias = array_merge($isOriginalName, $isAlias, $isVisibility, $isAliasType); 1119 1120 $useStatementIndex = 0; 1121 $aliasStatementIndex = 0; 1122 $useAliasContext = false; 1123 $useAsContext = false; 1124 1125 // start processing with next token 1126 if ($MACRO_TOKEN_ADVANCE() === false) { 1127 goto SCANNER_END; 1128 } 1129 1130 SCANNER_USE_TOP: 1131 1132 if ($tokenType === null) { 1133 if ($tokenContent === '{') { 1134 $useStatementIndex = 0; 1135 $useAliasContext = true; 1136 $infos[$infoIndex]['aliases'][$useStatementIndex] = [ 1137 'original' => null, 1138 'alias' => null, 1139 'visibility' => null, 1140 'type' => 'as', 1141 ]; 1142 } elseif ($tokenContent === '}') { 1143 $useAliasContext = false; 1144 goto SCANNER_USE_END; 1145 } elseif ($tokenContent === ';') { 1146 if ($useAliasContext === true) { 1147 $useStatementIndex++; 1148 $useAsContext = false; 1149 } 1150 // only end if we aren't inside braces 1151 if (false === $useAliasContext) { 1152 goto SCANNER_USE_END; 1153 } 1154 } elseif ($tokenContent === ',') { 1155 $useStatementIndex++; 1156 $infos[$infoIndex]['use_statements'][$useStatementIndex] = ''; 1157 } 1158 } 1159 1160 // ANALYZE 1161 if ($tokenType !== null) { 1162 // use context 1163 if (false === $useAliasContext) { 1164 if ($tokenType == T_NS_SEPARATOR || $tokenType == T_STRING) { 1165 $infos[$infoIndex]['use_statements'][$useStatementIndex] .= $tokenContent; 1166 } 1167 } else { 1168 if (in_array($tokenType, $isValidAlias) 1169 && empty($infos[$infoIndex]['aliases'][$useStatementIndex]) 1170 ) { 1171 $infos[$infoIndex]['aliases'][$useStatementIndex] = [ 1172 'original' => null, 1173 'visibility' => null, 1174 'alias' => null, 1175 'type' => null, 1176 ]; 1177 } 1178 1179 if ($tokenType == T_AS || $tokenType == T_INSTEADOF) { 1180 $useAsContext = true; 1181 $infos[$infoIndex]['aliases'][$useStatementIndex]['type'] = $tokenType == T_INSTEADOF 1182 ? 'insteadof' 1183 : 'as'; 1184 goto SCANNER_USE_CONTINUE; 1185 } 1186 1187 // in alias context 1188 if ($useAsContext === true && in_array($tokenType, $isAlias)) { 1189 $infos[$infoIndex]['aliases'][$useStatementIndex]['alias'] = $tokenContent; 1190 } elseif (in_array($tokenType, $isOriginalName)) { 1191 $infos[$infoIndex]['aliases'][$useStatementIndex]['original'] .= $tokenContent; 1192 } elseif (in_array($tokenType, $isVisibility)) { 1193 //add whitespace (will trim later) 1194 $infos[$infoIndex]['aliases'][$useStatementIndex]['visibility'] = $tokenType; 1195 } 1196 } 1197 } 1198 1199 SCANNER_USE_CONTINUE: 1200 1201 if ($MACRO_TOKEN_ADVANCE() === false) { 1202 goto SCANNER_END; 1203 } 1204 goto SCANNER_USE_TOP; 1205 1206 SCANNER_USE_END: 1207 1208 $MACRO_INFO_ADVANCE(); 1209 goto SCANNER_CLASS_BODY_CONTINUE; 1210 // goto no break needed 1211 1212 case T_DOC_COMMENT: 1213 case T_PUBLIC: 1214 case T_PROTECTED: 1215 case T_PRIVATE: 1216 case T_ABSTRACT: 1217 case T_FINAL: 1218 case T_VAR: 1219 case T_FUNCTION: 1220 $infos[$infoIndex] = [ 1221 'type' => null, 1222 'tokenStart' => $tokenIndex, 1223 'tokenEnd' => null, 1224 'lineStart' => $tokenLine, 1225 'lineEnd' => null, 1226 'name' => null, 1227 ]; 1228 1229 $memberContext = null; 1230 $methodBodyStarted = false; 1231 1232 SCANNER_CLASS_BODY_MEMBER_TOP: 1233 1234 if ($memberContext === 'method') { 1235 switch ($tokenContent) { 1236 case '{': 1237 $methodBodyStarted = true; 1238 $braceCount++; 1239 goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; 1240 // goto no break needed 1241 1242 case '}': 1243 $braceCount--; 1244 goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; 1245 // goto no break needed 1246 1247 case ';': 1248 $infos[$infoIndex]['tokenEnd'] = $tokenIndex; 1249 goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; 1250 // goto no break needed 1251 } 1252 } 1253 1254 if ($memberContext !== null) { 1255 if (($memberContext === 'property' && $tokenContent === ';') 1256 || ($memberContext === 'method' && $methodBodyStarted && $braceCount === 1) 1257 || ($memberContext === 'method' && $this->isInterface && $tokenContent === ';') 1258 ) { 1259 goto SCANNER_CLASS_BODY_MEMBER_END; 1260 } 1261 } 1262 1263 switch ($tokenType) { 1264 case T_CONST: 1265 $memberContext = 'constant'; 1266 $infos[$infoIndex]['type'] = 'constant'; 1267 goto SCANNER_CLASS_BODY_CONST_CONTINUE; 1268 //goto no break needed 1269 1270 case T_VARIABLE: 1271 if ($memberContext === null) { 1272 $memberContext = 'property'; 1273 $infos[$infoIndex]['type'] = 'property'; 1274 $infos[$infoIndex]['name'] = ltrim($tokenContent, '$'); 1275 } 1276 goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; 1277 // goto no break needed 1278 1279 case T_FUNCTION: 1280 $memberContext = 'method'; 1281 $infos[$infoIndex]['type'] = 'method'; 1282 goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; 1283 // goto no break needed 1284 1285 case T_STRING: 1286 if ($memberContext === 'method' && null === $infos[$infoIndex]['name']) { 1287 $infos[$infoIndex]['name'] = $tokenContent; 1288 } 1289 goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; 1290 // goto no break needed 1291 } 1292 1293 SCANNER_CLASS_BODY_MEMBER_CONTINUE: 1294 1295 if ($MACRO_TOKEN_ADVANCE() === false) { 1296 goto SCANNER_END; 1297 } 1298 goto SCANNER_CLASS_BODY_MEMBER_TOP; 1299 1300 SCANNER_CLASS_BODY_MEMBER_END: 1301 1302 $memberContext = null; 1303 $MACRO_INFO_ADVANCE(); 1304 goto SCANNER_CLASS_BODY_CONTINUE; 1305 // goto no break needed 1306 1307 case null: // no type, is a string 1308 switch ($tokenContent) { 1309 case '{': 1310 $braceCount++; 1311 goto SCANNER_CLASS_BODY_CONTINUE; 1312 // goto no break needed 1313 1314 case '}': 1315 $braceCount--; 1316 goto SCANNER_CLASS_BODY_CONTINUE; 1317 // goto no break needed 1318 } 1319 } 1320 1321 SCANNER_CLASS_BODY_CONTINUE: 1322 1323 if ($braceCount === 0 || $MACRO_TOKEN_ADVANCE() === false) { 1324 goto SCANNER_CONTINUE; 1325 } 1326 goto SCANNER_CLASS_BODY_TOP; 1327 1328 SCANNER_CLASS_BODY_END: 1329 1330 goto SCANNER_CONTINUE; 1331 } 1332 1333 SCANNER_CONTINUE: 1334 1335 if ($tokenContent === '}') { 1336 $this->lineEnd = $tokenLine; 1337 } 1338 1339 if ($MACRO_TOKEN_ADVANCE() === false) { 1340 goto SCANNER_END; 1341 } 1342 goto SCANNER_TOP; 1343 1344 SCANNER_END: 1345 1346 // process short names 1347 if ($this->nameInformation) { 1348 if ($this->shortParentClass) { 1349 $this->parentClass = $this->nameInformation->resolveName($this->shortParentClass); 1350 } 1351 if ($this->shortInterfaces) { 1352 foreach ($this->shortInterfaces as $siIndex => $si) { 1353 $this->interfaces[$siIndex] = $this->nameInformation->resolveName($si); 1354 } 1355 } 1356 } else { 1357 $this->parentClass = $this->shortParentClass; 1358 $this->interfaces = $this->shortInterfaces; 1359 } 1360 1361 $this->isScanned = true; 1362 } 1363 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |