[ Index ]

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


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1