[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/vendor/zendframework/zend-code/src/Scanner/ -> ClassScanner.php (source)

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


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1