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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

ClassScanner.php

Zuletzt modifiziert: 09.10.2024, 12:57 - Dateigröße: 39.29 KiB


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