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