Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
ClassScanner.php
0001 <?php
0002 /**
0003 * Zend Framework (http://framework.zend.com/)
0004 *
0005 * @link http://github.com/zendframework/zf2 for the canonical source repository
0006 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
0007 * @license http://framework.zend.com/license/new-bsd New BSD License
0008 */
0009
0010 namespace Zend\Code\Scanner;
0011
0012 use ReflectionClass;
0013 use Zend\Code\Annotation;
0014 use Zend\Code\Exception;
0015 use Zend\Code\NameInformation;
0016
0017 use function array_key_exists;
0018 use function array_merge;
0019 use function array_search;
0020 use function array_slice;
0021 use function array_values;
0022 use function define;
0023 use function defined;
0024 use function explode;
0025 use function in_array;
0026 use function is_array;
0027 use function is_int;
0028 use function is_object;
0029 use function is_string;
0030 use function ltrim;
0031 use function sprintf;
0032 use function substr_count;
0033 use function trigger_error;
0034
0035 class ClassScanner implements ScannerInterface
0036 {
0037 /**
0038 * @var bool
0039 */
0040 protected $isScanned = false;
0041
0042 /**
0043 * @var string
0044 */
0045 protected $docComment;
0046
0047 /**
0048 * @var string
0049 */
0050 protected $name;
0051
0052 /**
0053 * @var string
0054 */
0055 protected $shortName;
0056
0057 /**
0058 * @var int
0059 */
0060 protected $lineStart;
0061
0062 /**
0063 * @var int
0064 */
0065 protected $lineEnd;
0066
0067 /**
0068 * @var bool
0069 */
0070 protected $isTrait = false;
0071
0072 /**
0073 * @var bool
0074 */
0075 protected $isFinal = false;
0076
0077 /**
0078 * @var bool
0079 */
0080 protected $isAbstract = false;
0081
0082 /**
0083 * @var bool
0084 */
0085 protected $isInterface = false;
0086
0087 /**
0088 * @var string
0089 */
0090 protected $parentClass;
0091
0092 /**
0093 * @var string
0094 */
0095 protected $shortParentClass;
0096
0097 /**
0098 * @var array
0099 */
0100 protected $interfaces = [];
0101
0102 /**
0103 * @var array
0104 */
0105 protected $shortInterfaces = [];
0106
0107 /**
0108 * @var array
0109 */
0110 protected $tokens = [];
0111
0112 /**
0113 * @var NameInformation
0114 */
0115 protected $nameInformation;
0116
0117 /**
0118 * @var array
0119 */
0120 protected $infos = [];
0121
0122 /**
0123 * @var array
0124 */
0125 protected $traits = [];
0126
0127 /**
0128 * @var array
0129 */
0130 protected $methods = [];
0131
0132 /**
0133 * @param array $classTokens
0134 * @param NameInformation|null $nameInformation
0135 * @return ClassScanner
0136 */
0137 public function __construct(array $classTokens, NameInformation $nameInformation = null)
0138 {
0139 $this->tokens = $classTokens;
0140 $this->nameInformation = $nameInformation;
0141 }
0142
0143 /**
0144 * Get annotations
0145 *
0146 * @param Annotation\AnnotationManager $annotationManager
0147 * @return Annotation\AnnotationCollection
0148 */
0149 public function getAnnotations(Annotation\AnnotationManager $annotationManager)
0150 {
0151 if (($docComment = $this->getDocComment()) == '') {
0152 return false;
0153 }
0154
0155 return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
0156 }
0157
0158 /**
0159 * Return documentation comment
0160 *
0161 * @return null|string
0162 */
0163 public function getDocComment()
0164 {
0165 $this->scan();
0166
0167 return $this->docComment;
0168 }
0169
0170 /**
0171 * Return documentation block
0172 *
0173 * @return false|DocBlockScanner
0174 */
0175 public function getDocBlock()
0176 {
0177 if (! $docComment = $this->getDocComment()) {
0178 return false;
0179 }
0180
0181 return new DocBlockScanner($docComment);
0182 }
0183
0184 /**
0185 * Return a name of class
0186 *
0187 * @return null|string
0188 */
0189 public function getName()
0190 {
0191 $this->scan();
0192 return $this->name;
0193 }
0194
0195 /**
0196 * Return short name of class
0197 *
0198 * @return null|string
0199 */
0200 public function getShortName()
0201 {
0202 $this->scan();
0203 return $this->shortName;
0204 }
0205
0206 /**
0207 * Return number of first line
0208 *
0209 * @return int|null
0210 */
0211 public function getLineStart()
0212 {
0213 $this->scan();
0214 return $this->lineStart;
0215 }
0216
0217 /**
0218 * Return number of last line
0219 *
0220 * @return int|null
0221 */
0222 public function getLineEnd()
0223 {
0224 $this->scan();
0225 return $this->lineEnd;
0226 }
0227
0228 /**
0229 * Verify if class is final
0230 *
0231 * @return bool
0232 */
0233 public function isFinal()
0234 {
0235 $this->scan();
0236 return $this->isFinal;
0237 }
0238
0239 /**
0240 * Verify if class is a trait
0241 *
0242 * @return bool
0243 */
0244 public function isTrait()
0245 {
0246 $this->scan();
0247 return $this->isTrait;
0248 }
0249
0250 /**
0251 * Verify if class is instantiable
0252 *
0253 * @return bool
0254 */
0255 public function isInstantiable()
0256 {
0257 $this->scan();
0258 return ! $this->isAbstract && ! $this->isInterface && ! $this->isTrait;
0259 }
0260
0261 /**
0262 * Verify if class is an abstract class
0263 *
0264 * @return bool
0265 */
0266 public function isAbstract()
0267 {
0268 $this->scan();
0269 return $this->isAbstract;
0270 }
0271
0272 /**
0273 * Verify if class is an interface
0274 *
0275 * @return bool
0276 */
0277 public function isInterface()
0278 {
0279 $this->scan();
0280 return $this->isInterface;
0281 }
0282
0283 /**
0284 * Verify if class has parent
0285 *
0286 * @return bool
0287 */
0288 public function hasParentClass()
0289 {
0290 $this->scan();
0291 return $this->parentClass !== null;
0292 }
0293
0294 /**
0295 * Return a name of parent class
0296 *
0297 * @return null|string
0298 */
0299 public function getParentClass()
0300 {
0301 $this->scan();
0302 return $this->parentClass;
0303 }
0304
0305 /**
0306 * Return a list of interface names
0307 *
0308 * @return array
0309 */
0310 public function getInterfaces()
0311 {
0312 $this->scan();
0313 return $this->interfaces;
0314 }
0315
0316 /**
0317 * Return a list of constant names
0318 *
0319 * @return array
0320 */
0321 public function getConstantNames()
0322 {
0323 $this->scan();
0324
0325 $return = [];
0326 foreach ($this->infos as $info) {
0327 if ($info['type'] != 'constant') {
0328 continue;
0329 }
0330
0331 $return[] = $info['name'];
0332 }
0333
0334 return $return;
0335 }
0336
0337 /**
0338 * Return a list of constants
0339 *
0340 * @param bool $namesOnly Set false to return instances of ConstantScanner
0341 * @return array|ConstantScanner[]
0342 */
0343 public function getConstants($namesOnly = true)
0344 {
0345 if (true === $namesOnly) {
0346 trigger_error('Use method getConstantNames() instead', E_USER_DEPRECATED);
0347 return $this->getConstantNames();
0348 }
0349
0350 $this->scan();
0351
0352 $return = [];
0353 foreach ($this->infos as $info) {
0354 if ($info['type'] != 'constant') {
0355 continue;
0356 }
0357
0358 $return[] = $this->getConstant($info['name']);
0359 }
0360
0361 return $return;
0362 }
0363
0364 /**
0365 * Return a single constant by given name or index of info
0366 *
0367 * @param string|int $constantNameOrInfoIndex
0368 * @throws Exception\InvalidArgumentException
0369 * @return bool|ConstantScanner
0370 */
0371 public function getConstant($constantNameOrInfoIndex)
0372 {
0373 $this->scan();
0374
0375 if (is_int($constantNameOrInfoIndex)) {
0376 $info = $this->infos[$constantNameOrInfoIndex];
0377 if ($info['type'] != 'constant') {
0378 throw new Exception\InvalidArgumentException('Index of info offset is not about a constant');
0379 }
0380 } elseif (is_string($constantNameOrInfoIndex)) {
0381 $constantFound = false;
0382 foreach ($this->infos as $info) {
0383 if ($info['type'] === 'constant' && $info['name'] === $constantNameOrInfoIndex) {
0384 $constantFound = true;
0385 break;
0386 }
0387 }
0388 if (! $constantFound) {
0389 return false;
0390 }
0391 } else {
0392 throw new Exception\InvalidArgumentException(
0393 'Invalid constant name of info index type. Must be of type int or string'
0394 );
0395 }
0396 if (! isset($info)) {
0397 return false;
0398 }
0399 $p = new ConstantScanner(
0400 array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1),
0401 $this->nameInformation
0402 );
0403 $p->setClass($this->name);
0404 $p->setScannerClass($this);
0405 return $p;
0406 }
0407
0408 /**
0409 * Verify if class has constant
0410 *
0411 * @param string $name
0412 * @return bool
0413 */
0414 public function hasConstant($name)
0415 {
0416 $this->scan();
0417
0418 foreach ($this->infos as $info) {
0419 if ($info['type'] === 'constant' && $info['name'] === $name) {
0420 return true;
0421 }
0422 }
0423
0424 return false;
0425 }
0426
0427 /**
0428 * Return a list of property names
0429 *
0430 * @return array
0431 */
0432 public function getPropertyNames()
0433 {
0434 $this->scan();
0435
0436 $return = [];
0437 foreach ($this->infos as $info) {
0438 if ($info['type'] != 'property') {
0439 continue;
0440 }
0441
0442 $return[] = $info['name'];
0443 }
0444
0445 return $return;
0446 }
0447
0448 /**
0449 * Return a list of properties
0450 *
0451 * @return PropertyScanner[]
0452 */
0453 public function getProperties()
0454 {
0455 $this->scan();
0456
0457 $return = [];
0458 foreach ($this->infos as $info) {
0459 if ($info['type'] != 'property') {
0460 continue;
0461 }
0462
0463 $return[] = $this->getProperty($info['name']);
0464 }
0465
0466 return $return;
0467 }
0468
0469 /**
0470 * Return a single property by given name or index of info
0471 *
0472 * @param string|int $propertyNameOrInfoIndex
0473 * @throws Exception\InvalidArgumentException
0474 * @return bool|PropertyScanner
0475 */
0476 public function getProperty($propertyNameOrInfoIndex)
0477 {
0478 $this->scan();
0479
0480 if (is_int($propertyNameOrInfoIndex)) {
0481 $info = $this->infos[$propertyNameOrInfoIndex];
0482 if ($info['type'] != 'property') {
0483 throw new Exception\InvalidArgumentException('Index of info offset is not about a property');
0484 }
0485 } elseif (is_string($propertyNameOrInfoIndex)) {
0486 $propertyFound = false;
0487 foreach ($this->infos as $info) {
0488 if ($info['type'] === 'property' && $info['name'] === $propertyNameOrInfoIndex) {
0489 $propertyFound = true;
0490 break;
0491 }
0492 }
0493 if (! $propertyFound) {
0494 return false;
0495 }
0496 } else {
0497 throw new Exception\InvalidArgumentException(
0498 'Invalid property name of info index type. Must be of type int or string'
0499 );
0500 }
0501 if (! isset($info)) {
0502 return false;
0503 }
0504 $p = new PropertyScanner(
0505 array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1),
0506 $this->nameInformation
0507 );
0508 $p->setClass($this->name);
0509 $p->setScannerClass($this);
0510 return $p;
0511 }
0512
0513 /**
0514 * Verify if class has property
0515 *
0516 * @param string $name
0517 * @return bool
0518 */
0519 public function hasProperty($name)
0520 {
0521 $this->scan();
0522
0523 foreach ($this->infos as $info) {
0524 if ($info['type'] === 'property' && $info['name'] === $name) {
0525 return true;
0526 }
0527 }
0528
0529 return false;
0530 }
0531
0532 /**
0533 * Retrieve any traits used by the class.
0534 *
0535 * @return ClassScanner[]
0536 */
0537 public function getTraits()
0538 {
0539 if (! empty($this->traits)) {
0540 return $this->traits;
0541 }
0542
0543 // get list of trait names
0544 $traitNames = $this->getTraitNames();
0545 foreach ($traitNames as $traitName) {
0546 $r = new ReflectionClass($traitName);
0547 if (! $r->isTrait()) {
0548 throw new Exception\RuntimeException(sprintf(
0549 'Non-trait class detected as a trait: %s',
0550 $traitName
0551 ));
0552 }
0553 $fileName = $r->getFileName();
0554
0555 $file = new FileScanner($fileName);
0556 $this->traits[] = $file->getClass($traitName);
0557 }
0558
0559 return $this->traits;
0560 }
0561
0562 /**
0563 * Retrieve a list of trait names used by this class.
0564 *
0565 * @return array
0566 */
0567 public function getTraitNames()
0568 {
0569 $this->scan();
0570
0571 $return = [];
0572 foreach ($this->infos as $info) {
0573 if ($info['type'] !== 'use') {
0574 continue;
0575 }
0576
0577 if (is_array($info['use_statements'])) {
0578 foreach ($info['use_statements'] as $trait) {
0579 $traitName = $trait;
0580 if ($this->nameInformation instanceof NameInformation) {
0581 $traitName = $this->nameInformation->resolveName($traitName);
0582 }
0583 $return[] = $traitName;
0584 }
0585 }
0586 }
0587
0588 return $return;
0589 }
0590
0591 /**
0592 * Retrieve a list of aliased traits used by the class.
0593 *
0594 * @return array
0595 */
0596 public function getTraitAliases()
0597 {
0598 $this->scan();
0599
0600 $return = [];
0601 foreach ($this->infos as $info) {
0602 if ($info['type'] !== 'use') {
0603 continue;
0604 }
0605
0606 if (is_array($info['aliases'])) {
0607 foreach ($info['aliases'] as $alias) {
0608 if (null === $alias
0609 || (! empty($alias['type']) && $alias['type'] !== 'as')
0610 ) {
0611 continue;
0612 }
0613
0614 // attempt to get fqcn
0615 list($trait, $method) = explode('::', $alias['original']);
0616 if ($this->nameInformation instanceof NameInformation) {
0617 $trait = $this->nameInformation->resolveName($trait);
0618 }
0619
0620 $return[$alias['alias']] = $trait . '::' . $method;
0621 }
0622 }
0623 }
0624
0625 return $return;
0626 }
0627
0628 /**
0629 * Retrieve visibility for a given alias.
0630 *
0631 * @param mixed $aliasName
0632 * @return string
0633 */
0634 protected function getVisibilityForAlias($aliasName)
0635 {
0636 $this->scan();
0637
0638 $return = null;
0639 foreach ($this->infos as $info) {
0640 if ($info['type'] !== 'use') {
0641 continue;
0642 }
0643
0644 if (is_array($info['aliases'])) {
0645 foreach ($info['aliases'] as $alias) {
0646 if (null === $alias
0647 && (! empty($alias['type']) && $alias['type'] !== 'as')
0648 ) {
0649 continue;
0650 }
0651
0652 if ($alias['alias'] === $aliasName) {
0653 $return = $alias['visibility'];
0654 break 2;
0655 }
0656 }
0657 }
0658 }
0659
0660 return $return;
0661 }
0662
0663 /**
0664 * Return an array of key = trait to keep, value = trait::method to ignore
0665 *
0666 * @return array
0667 */
0668 protected function getBlockedTraitMethods()
0669 {
0670 $this->scan();
0671
0672 $return = [];
0673 foreach ($this->infos as $info) {
0674 if ($info['type'] !== 'use') {
0675 continue;
0676 }
0677
0678 if (is_array($info['aliases'])) {
0679 foreach ($info['aliases'] as $alias) {
0680 if (null === $alias
0681 || (! empty($alias['type']) && $alias['type'] !== 'insteadof')
0682 ) {
0683 continue;
0684 }
0685
0686 // attempt to get fqcn
0687 list($trait, $method) = explode('::', $alias['original']);
0688 if ($this->nameInformation instanceof NameInformation) {
0689 $trait = $this->nameInformation->resolveName($alias['alias']);
0690 }
0691
0692 $return[] = $trait . '::' . $method;
0693 }
0694 }
0695 }
0696
0697 return $return;
0698 }
0699
0700 /**
0701 * Return a list of method names
0702 *
0703 * @return array
0704 */
0705 public function getMethodNames()
0706 {
0707 $this->scan();
0708
0709 $methods = $this->getMethods();
0710 $return = [];
0711 foreach ($methods as $method) {
0712 $return[] = $method->getName();
0713 }
0714
0715 return $return;
0716 }
0717
0718 /**
0719 * Return a list of methods
0720 *
0721 * @return MethodScanner[]
0722 */
0723 public function getMethods()
0724 {
0725 $this->scan();
0726
0727 if (! empty($this->methods)) {
0728 return $this->methods;
0729 }
0730
0731 foreach ($this->infos as $info) {
0732 if ($info['type'] !== 'method' && $info['type'] !== 'use') {
0733 continue;
0734 }
0735
0736 // Merge in trait methods
0737 if ($info['type'] === 'use') {
0738 $traitMethods = [];
0739 $traits = $this->getTraits();
0740 $insteadof = $this->getBlockedTraitMethods();
0741 $aliases = $this->getTraitAliases();
0742
0743 foreach ($traits as $trait) {
0744 $tempMethods = $trait->getMethods();
0745 foreach ($tempMethods as $tempMethod) {
0746 $methodFullName = $trait->getName() . '::' . $tempMethod->getName();
0747 $methodAlias = array_search($methodFullName, $aliases);
0748
0749 if (false !== $methodAlias) {
0750 // trait::method is aliased
0751 // clone the tempMethod as we need to change
0752 // the name and possibly the visibility of the
0753 // scanned method.
0754 //
0755 // @todo setName and setVisibility were added to
0756 // MethodScanner to accomplish this, may not be the
0757 // best option, could use ReflectionClass instead?
0758 $newMethod = clone $tempMethod;
0759 $newMethod->setName($methodAlias);
0760
0761 // if visibility exists, change it on the MethodScanner
0762 $visibility = $this->getVisibilityForAlias($methodAlias);
0763 if (null !== $visibility) {
0764 $newMethod->setVisibility($visibility);
0765 }
0766 $traitMethods[$methodAlias] = $newMethod;
0767 } elseif (in_array($methodFullName, $insteadof)) {
0768 // ignore overridden methods
0769 continue;
0770 } else {
0771 if (array_key_exists($tempMethod->getName(), $traitMethods)) {
0772 throw new Exception\RuntimeException(sprintf(
0773 'Trait method %s has not been applied because there are'
0774 . ' collisions with other trait methods see: (insteadof OR as)',
0775 $tempMethod->getName()
0776 ));
0777 }
0778
0779 $traitMethods[$tempMethod->getName()] = $tempMethod;
0780 }
0781 }
0782 }
0783
0784 $this->methods = array_merge($this->methods, array_values($traitMethods));
0785 continue;
0786 }
0787
0788 $m = new MethodScanner(
0789 array_slice(
0790 $this->tokens,
0791 $info['tokenStart'],
0792 $info['tokenEnd'] - $info['tokenStart'] + 1
0793 ),
0794 $this->nameInformation
0795 );
0796 $m->setClass($this->name);
0797 $m->setScannerClass($this);
0798
0799 $this->methods[] = $m;
0800 }
0801
0802 return $this->methods;
0803 }
0804
0805 /**
0806 * Return a single method by given name or index of info
0807 *
0808 * @param string|int $methodNameOrInfoIndex
0809 * @throws Exception\InvalidArgumentException
0810 * @return MethodScanner
0811 */
0812 public function getMethod($methodNameOrInfoIndex)
0813 {
0814 $this->scan();
0815
0816 if (is_int($methodNameOrInfoIndex)) {
0817 $info = $this->infos[$methodNameOrInfoIndex];
0818 if ($info['type'] != 'method') {
0819 throw new Exception\InvalidArgumentException('Index of info offset is not about a method');
0820 }
0821 $methodNameOrInfoIndex = $info['name'];
0822 }
0823
0824 $returnMethod = false;
0825 $methods = $this->getMethods();
0826 foreach ($methods as $method) {
0827 if ($method->getName() === $methodNameOrInfoIndex) {
0828 $returnMethod = $method;
0829 break;
0830 }
0831 }
0832
0833 return $returnMethod;
0834 }
0835
0836 /**
0837 * Verify if class has method by given name
0838 *
0839 * @param string $name
0840 * @return bool
0841 */
0842 public function hasMethod($name)
0843 {
0844 $this->scan();
0845
0846 return is_object($this->getMethod($name));
0847 }
0848
0849 public static function export()
0850 {
0851 // @todo
0852 }
0853
0854 public function __toString()
0855 {
0856 // @todo
0857 }
0858
0859 /**
0860 * Scan tokens
0861 *
0862 * @return void
0863 * @throws Exception\RuntimeException
0864 */
0865 protected function scan()
0866 {
0867 if ($this->isScanned) {
0868 return;
0869 }
0870
0871 if (! $this->tokens) {
0872 throw new Exception\RuntimeException('No tokens were provided');
0873 }
0874
0875 /**
0876 * Variables & Setup
0877 */
0878 $tokens = &$this->tokens; // localize
0879 $infos = &$this->infos; // localize
0880 $tokenIndex = null;
0881 $token = null;
0882 $tokenType = null;
0883 $tokenContent = null;
0884 $tokenLine = null;
0885 $namespace = null;
0886 $infoIndex = 0;
0887 $braceCount = 0;
0888
0889 /*
0890 * MACRO creation
0891 */
0892 $MACRO_TOKEN_ADVANCE = function () use (
0893 &$tokens,
0894 &$tokenIndex,
0895 &$token,
0896 &$tokenType,
0897 &$tokenContent,
0898 &$tokenLine
0899 ) {
0900 static $lastTokenArray = null;
0901 $tokenIndex = $tokenIndex === null ? 0 : $tokenIndex + 1;
0902 if (! isset($tokens[$tokenIndex])) {
0903 $token = false;
0904 $tokenContent = false;
0905 $tokenType = false;
0906 $tokenLine = false;
0907
0908 return false;
0909 }
0910 $token = $tokens[$tokenIndex];
0911
0912 if (is_string($token)) {
0913 $tokenType = null;
0914 $tokenContent = $token;
0915 $tokenLine += substr_count(
0916 $lastTokenArray[1] ?? '',
0917 "\n"
0918 ); // adjust token line by last known newline count
0919 } else {
0920 $lastTokenArray = $token;
0921 [$tokenType, $tokenContent, $tokenLine] = $token;
0922 }
0923
0924 return $tokenIndex;
0925 };
0926 $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
0927 $infos[$infoIndex]['tokenEnd'] = $tokenIndex;
0928 $infos[$infoIndex]['lineEnd'] = $tokenLine;
0929 $infoIndex++;
0930
0931 return $infoIndex;
0932 };
0933
0934 /**
0935 * START FINITE STATE MACHINE FOR SCANNING TOKENS
0936 */
0937 // Initialize token
0938 $MACRO_TOKEN_ADVANCE();
0939
0940 SCANNER_TOP:
0941
0942 switch ($tokenType) {
0943 case T_DOC_COMMENT:
0944 $this->docComment = $tokenContent;
0945 goto SCANNER_CONTINUE;
0946 //goto no break needed
0947
0948 case T_FINAL:
0949 case T_ABSTRACT:
0950 case T_CLASS:
0951 case T_INTERFACE:
0952 case T_TRAIT:
0953 // CLASS INFORMATION
0954
0955 $classContext = null;
0956 $classInterfaceIndex = 0;
0957
0958 SCANNER_CLASS_INFO_TOP:
0959
0960 if (is_string($tokens[$tokenIndex + 1]) && $tokens[$tokenIndex + 1] === '{') {
0961 goto SCANNER_CLASS_INFO_END;
0962 }
0963
0964 $this->lineStart = $tokenLine;
0965
0966 switch ($tokenType) {
0967 case T_FINAL:
0968 $this->isFinal = true;
0969 goto SCANNER_CLASS_INFO_CONTINUE;
0970 // goto no break needed
0971
0972 case T_ABSTRACT:
0973 $this->isAbstract = true;
0974 goto SCANNER_CLASS_INFO_CONTINUE;
0975 // goto no break needed
0976
0977 case T_TRAIT:
0978 $this->isTrait = true;
0979 $this->shortName = $tokens[$tokenIndex + 2][1];
0980 if ($this->nameInformation && $this->nameInformation->hasNamespace()) {
0981 $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName;
0982 } else {
0983 $this->name = $this->shortName;
0984 }
0985 goto SCANNER_CLASS_INFO_CONTINUE;
0986 // goto no break needed
0987
0988 case T_INTERFACE:
0989 $this->isInterface = true;
0990 // fall-through
0991 case T_CLASS:
0992 $this->shortName = $tokens[$tokenIndex + 2][1];
0993 if ($this->nameInformation && $this->nameInformation->hasNamespace()) {
0994 $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName;
0995 } else {
0996 $this->name = $this->shortName;
0997 }
0998 goto SCANNER_CLASS_INFO_CONTINUE;
0999 // 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 }
1364