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