[ Index ]

PHP Cross Reference of phpBB-3.3.12-deutsch

title

Body

[close]

/vendor/zendframework/zend-code/src/Scanner/ -> TokenArrayScanner.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 Zend\Code\Annotation\AnnotationManager;
  13  use Zend\Code\Exception;
  14  use Zend\Code\NameInformation;
  15  
  16  use function array_shift;
  17  use function array_slice;
  18  use function in_array;
  19  use function is_array;
  20  use function is_int;
  21  use function is_string;
  22  
  23  class TokenArrayScanner implements ScannerInterface
  24  {
  25      /**
  26       * @var bool
  27       */
  28      protected $isScanned = false;
  29  
  30      /**
  31       * @var array
  32       */
  33      protected $tokens = [];
  34  
  35      /**
  36       * @var null
  37       */
  38      protected $docComment;
  39  
  40      /**
  41       * @var NameInformation
  42       */
  43      protected $nameInformation;
  44  
  45      /**
  46       * @var array
  47       */
  48      protected $infos = [];
  49  
  50      /**
  51       * @var AnnotationManager
  52       */
  53      protected $annotationManager;
  54  
  55      /**
  56       * @param null|array $tokens
  57       * @param null|AnnotationManager $annotationManager
  58       */
  59      public function __construct($tokens, AnnotationManager $annotationManager = null)
  60      {
  61          $this->tokens            = $tokens;
  62          $this->annotationManager = $annotationManager;
  63      }
  64  
  65      /**
  66       * @return AnnotationManager
  67       */
  68      public function getAnnotationManager()
  69      {
  70          return $this->annotationManager;
  71      }
  72  
  73      /**
  74       * Get doc comment
  75       *
  76       * @todo Assignment of $this->docComment should probably be done in scan()
  77       *       and then $this->getDocComment() just retrieves it.
  78       *
  79       * @return string|null
  80       */
  81      public function getDocComment()
  82      {
  83          foreach ($this->tokens as $token) {
  84              $type    = $token[0];
  85              $value   = $token[1];
  86              if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
  87                  continue;
  88              } elseif ($type == T_DOC_COMMENT) {
  89                  $this->docComment = $value;
  90  
  91                  return $this->docComment;
  92              } else {
  93                  // Only whitespace is allowed before file docblocks
  94                  return;
  95              }
  96          }
  97      }
  98  
  99      /**
 100       * @return array
 101       */
 102      public function getNamespaces()
 103      {
 104          $this->scan();
 105  
 106          $namespaces = [];
 107          foreach ($this->infos as $info) {
 108              if ($info['type'] == 'namespace') {
 109                  $namespaces[] = $info['namespace'];
 110              }
 111          }
 112  
 113          return $namespaces;
 114      }
 115  
 116      /**
 117       * @param  null|string $namespace
 118       * @return array|null
 119       */
 120      public function getUses($namespace = null)
 121      {
 122          $this->scan();
 123  
 124          return $this->getUsesNoScan($namespace);
 125      }
 126  
 127      /**
 128       * @return void
 129       */
 130      public function getIncludes()
 131      {
 132          $this->scan();
 133          // @todo Implement getIncludes() in TokenArrayScanner
 134      }
 135  
 136      /**
 137       * @return array
 138       */
 139      public function getClassNames()
 140      {
 141          $this->scan();
 142  
 143          $return = [];
 144          foreach ($this->infos as $info) {
 145              if ($info['type'] != 'class') {
 146                  continue;
 147              }
 148  
 149              $return[] = $info['name'];
 150          }
 151  
 152          return $return;
 153      }
 154  
 155      /**
 156       * @return ClassScanner[]
 157       */
 158      public function getClasses()
 159      {
 160          $this->scan();
 161  
 162          $return = [];
 163          foreach ($this->infos as $info) {
 164              if ($info['type'] != 'class') {
 165                  continue;
 166              }
 167  
 168              $return[] = $this->getClass($info['name']);
 169          }
 170  
 171          return $return;
 172      }
 173  
 174      /**
 175       * Return the class object from this scanner
 176       *
 177       * @param  string|int $name
 178       * @throws Exception\InvalidArgumentException
 179       * @return ClassScanner|false
 180       */
 181      public function getClass($name)
 182      {
 183          $this->scan();
 184  
 185          if (is_int($name)) {
 186              $info = $this->infos[$name];
 187              if ($info['type'] != 'class') {
 188                  throw new Exception\InvalidArgumentException('Index of info offset is not about a class');
 189              }
 190          } elseif (is_string($name)) {
 191              $classFound = false;
 192              foreach ($this->infos as $info) {
 193                  if ($info['type'] === 'class' && $info['name'] === $name) {
 194                      $classFound = true;
 195                      break;
 196                  }
 197              }
 198  
 199              if (! $classFound) {
 200                  return false;
 201              }
 202          }
 203  
 204          return new ClassScanner(
 205              array_slice(
 206                  $this->tokens,
 207                  $info['tokenStart'],
 208                  $info['tokenEnd'] - $info['tokenStart'] + 1
 209              ), // zero indexed array
 210              new NameInformation($info['namespace'], $info['uses'])
 211          );
 212      }
 213  
 214      /**
 215       * @param  string $className
 216       * @return bool|null|NameInformation
 217       */
 218      public function getClassNameInformation($className)
 219      {
 220          $this->scan();
 221  
 222          $classFound = false;
 223          foreach ($this->infos as $info) {
 224              if ($info['type'] === 'class' && $info['name'] === $className) {
 225                  $classFound = true;
 226                  break;
 227              }
 228          }
 229  
 230          if (! $classFound) {
 231              return false;
 232          }
 233  
 234          if (! isset($info)) {
 235              return;
 236          }
 237  
 238          return new NameInformation($info['namespace'], $info['uses']);
 239      }
 240  
 241      /**
 242       * @return array
 243       */
 244      public function getFunctionNames()
 245      {
 246          $this->scan();
 247          $functionNames = [];
 248          foreach ($this->infos as $info) {
 249              if ($info['type'] == 'function') {
 250                  $functionNames[] = $info['name'];
 251              }
 252          }
 253  
 254          return $functionNames;
 255      }
 256  
 257      /**
 258       * @return array
 259       */
 260      public function getFunctions()
 261      {
 262          $this->scan();
 263  
 264          $functions = [];
 265  //        foreach ($this->infos as $info) {
 266  //            if ($info['type'] == 'function') {
 267  //                // @todo $functions[] = new FunctionScanner($info['name']);
 268  //            }
 269  //        }
 270  
 271          return $functions;
 272      }
 273  
 274      /**
 275       * Export
 276       *
 277       * @param mixed $tokens
 278       */
 279      public static function export($tokens)
 280      {
 281          // @todo
 282      }
 283  
 284      public function __toString()
 285      {
 286          // @todo
 287      }
 288  
 289      /**
 290       * Scan
 291       *
 292       * @todo: $this->docComment should be assigned for valid docblock during
 293       *        the scan instead of $this->getDocComment() (starting with
 294       *        T_DOC_COMMENT case)
 295       *
 296       * @throws Exception\RuntimeException
 297       */
 298      protected function scan()
 299      {
 300          if ($this->isScanned) {
 301              return;
 302          }
 303  
 304          if (! $this->tokens) {
 305              throw new Exception\RuntimeException('No tokens were provided');
 306          }
 307  
 308          /**
 309           * Variables & Setup
 310           */
 311          $tokens          = &$this->tokens; // localize
 312          $infos           = &$this->infos; // localize
 313          $tokenIndex      = null;
 314          $token           = null;
 315          $tokenType       = null;
 316          $tokenContent    = null;
 317          $tokenLine       = null;
 318          $namespace       = null;
 319          $docCommentIndex = false;
 320          $infoIndex       = 0;
 321  
 322          /*
 323           * MACRO creation
 324           */
 325          $MACRO_TOKEN_ADVANCE = function () use (
 326              &$tokens,
 327              &$tokenIndex,
 328              &$token,
 329              &$tokenType,
 330              &$tokenContent,
 331              &$tokenLine
 332          ) {
 333              $tokenIndex = $tokenIndex === null ? 0 : $tokenIndex + 1;
 334              if (! isset($tokens[$tokenIndex])) {
 335                  $token        = false;
 336                  $tokenContent = false;
 337                  $tokenType    = false;
 338                  $tokenLine    = false;
 339  
 340                  return false;
 341              }
 342              if (is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"') {
 343                  do {
 344                      $tokenIndex++;
 345                  } while (! (is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"'));
 346              }
 347              $token = $tokens[$tokenIndex];
 348              if (is_array($token)) {
 349                  list($tokenType, $tokenContent, $tokenLine) = $token;
 350              } else {
 351                  $tokenType    = null;
 352                  $tokenContent = $token;
 353              }
 354  
 355              return $tokenIndex;
 356          };
 357          $MACRO_TOKEN_LOGICAL_START_INDEX = function () use (&$tokenIndex, &$docCommentIndex) {
 358              return $docCommentIndex === false ? $tokenIndex : $docCommentIndex;
 359          };
 360          $MACRO_DOC_COMMENT_START = function () use (&$tokenIndex, &$docCommentIndex) {
 361              $docCommentIndex = $tokenIndex;
 362  
 363              return $docCommentIndex;
 364          };
 365          $MACRO_DOC_COMMENT_VALIDATE = function () use (&$tokenType, &$docCommentIndex) {
 366              static $validTrailingTokens = null;
 367              if ($validTrailingTokens === null) {
 368                  $validTrailingTokens = [T_WHITESPACE, T_FINAL, T_ABSTRACT, T_INTERFACE, T_CLASS, T_FUNCTION];
 369              }
 370              if ($docCommentIndex !== false && ! in_array($tokenType, $validTrailingTokens)) {
 371                  $docCommentIndex = false;
 372              }
 373  
 374              return $docCommentIndex;
 375          };
 376          $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
 377              $infos[$infoIndex]['tokenEnd'] = $tokenIndex;
 378              $infos[$infoIndex]['lineEnd']  = $tokenLine;
 379              $infoIndex++;
 380  
 381              return $infoIndex;
 382          };
 383  
 384          /**
 385           * START FINITE STATE MACHINE FOR SCANNING TOKENS
 386           */
 387          // Initialize token
 388          $MACRO_TOKEN_ADVANCE();
 389  
 390          SCANNER_TOP:
 391  
 392          if ($token === false) {
 393              goto SCANNER_END;
 394          }
 395  
 396          // Validate current doc comment index
 397          $MACRO_DOC_COMMENT_VALIDATE();
 398  
 399          switch ($tokenType) {
 400              case T_DOC_COMMENT:
 401                  $MACRO_DOC_COMMENT_START();
 402                  goto SCANNER_CONTINUE;
 403                  // goto no break needed
 404  
 405              case T_NAMESPACE:
 406                  $infos[$infoIndex] = [
 407                      'type'       => 'namespace',
 408                      'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(),
 409                      'tokenEnd'   => null,
 410                      'lineStart'  => $token[2],
 411                      'lineEnd'    => null,
 412                      'namespace'  => null,
 413                  ];
 414  
 415                  // start processing with next token
 416                  if ($MACRO_TOKEN_ADVANCE() === false) {
 417                      goto SCANNER_END;
 418                  }
 419  
 420                  SCANNER_NAMESPACE_TOP:
 421  
 422                  if (($tokenType === null && $tokenContent === ';') || $tokenContent === '{') {
 423                      goto SCANNER_NAMESPACE_END;
 424                  }
 425  
 426                  if ($tokenType === T_WHITESPACE) {
 427                      goto SCANNER_NAMESPACE_CONTINUE;
 428                  }
 429  
 430                  if ($tokenType === T_NS_SEPARATOR || $tokenType === T_STRING) {
 431                      $infos[$infoIndex]['namespace'] .= $tokenContent;
 432                  }
 433  
 434                  SCANNER_NAMESPACE_CONTINUE:
 435  
 436                  if ($MACRO_TOKEN_ADVANCE() === false) {
 437                      goto SCANNER_END;
 438                  }
 439                  goto SCANNER_NAMESPACE_TOP;
 440  
 441                  SCANNER_NAMESPACE_END:
 442  
 443                  $namespace = $infos[$infoIndex]['namespace'];
 444  
 445                  $MACRO_INFO_ADVANCE();
 446                  goto SCANNER_CONTINUE;
 447                  // goto no break needed
 448  
 449              case T_USE:
 450                  $infos[$infoIndex] = [
 451                      'type'       => 'use',
 452                      'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(),
 453                      'tokenEnd'   => null,
 454                      'lineStart'  => $tokens[$tokenIndex][2],
 455                      'lineEnd'    => null,
 456                      'namespace'  => $namespace,
 457                      'statements' => [
 458                          0 => [
 459                              'use' => null,
 460                              'as'  => null,
 461                          ],
 462                      ],
 463                  ];
 464  
 465                  $useStatementIndex = 0;
 466                  $useAsContext      = false;
 467  
 468                  // start processing with next token
 469                  if ($MACRO_TOKEN_ADVANCE() === false) {
 470                      goto SCANNER_END;
 471                  }
 472  
 473                  SCANNER_USE_TOP:
 474  
 475                  if ($tokenType === null) {
 476                      if ($tokenContent === ';') {
 477                          goto SCANNER_USE_END;
 478                      } elseif ($tokenContent === ',') {
 479                          $useAsContext = false;
 480                          $useStatementIndex++;
 481                          $infos[$infoIndex]['statements'][$useStatementIndex] = [
 482                              'use' => null,
 483                              'as'  => null,
 484                          ];
 485                      }
 486                  }
 487  
 488                  // ANALYZE
 489                  if ($tokenType !== null) {
 490                      if ($tokenType == T_AS) {
 491                          $useAsContext = true;
 492                          goto SCANNER_USE_CONTINUE;
 493                      }
 494  
 495                      if ($tokenType == T_NS_SEPARATOR || $tokenType == T_STRING) {
 496                          if ($useAsContext == false) {
 497                              $infos[$infoIndex]['statements'][$useStatementIndex]['use'] .= $tokenContent;
 498                          } else {
 499                              $infos[$infoIndex]['statements'][$useStatementIndex]['as'] = $tokenContent;
 500                          }
 501                      }
 502                  }
 503  
 504                  SCANNER_USE_CONTINUE:
 505  
 506                  if ($MACRO_TOKEN_ADVANCE() === false) {
 507                      goto SCANNER_END;
 508                  }
 509                  goto SCANNER_USE_TOP;
 510  
 511                  SCANNER_USE_END:
 512  
 513                  $MACRO_INFO_ADVANCE();
 514                  goto SCANNER_CONTINUE;
 515                  // goto no break needed
 516  
 517              case T_INCLUDE:
 518              case T_INCLUDE_ONCE:
 519              case T_REQUIRE:
 520              case T_REQUIRE_ONCE:
 521                  // Static for performance
 522                  static $includeTypes = [
 523                      T_INCLUDE      => 'include',
 524                      T_INCLUDE_ONCE => 'include_once',
 525                      T_REQUIRE      => 'require',
 526                      T_REQUIRE_ONCE => 'require_once',
 527                  ];
 528  
 529                  $infos[$infoIndex] = [
 530                      'type'        => 'include',
 531                      'tokenStart'  => $MACRO_TOKEN_LOGICAL_START_INDEX(),
 532                      'tokenEnd'    => null,
 533                      'lineStart'   => $tokens[$tokenIndex][2],
 534                      'lineEnd'     => null,
 535                      'includeType' => $includeTypes[$tokens[$tokenIndex][0]],
 536                      'path'        => '',
 537                  ];
 538  
 539                  // start processing with next token
 540                  if ($MACRO_TOKEN_ADVANCE() === false) {
 541                      goto SCANNER_END;
 542                  }
 543  
 544                  SCANNER_INCLUDE_TOP:
 545  
 546                  if ($tokenType === null && $tokenContent === ';') {
 547                      goto SCANNER_INCLUDE_END;
 548                  }
 549  
 550                  $infos[$infoIndex]['path'] .= $tokenContent;
 551  
 552                  SCANNER_INCLUDE_CONTINUE:
 553  
 554                  if ($MACRO_TOKEN_ADVANCE() === false) {
 555                      goto SCANNER_END;
 556                  }
 557                  goto SCANNER_INCLUDE_TOP;
 558  
 559                  SCANNER_INCLUDE_END:
 560  
 561                  $MACRO_INFO_ADVANCE();
 562                  goto SCANNER_CONTINUE;
 563                  // goto no break needed
 564  
 565              case T_FUNCTION:
 566              case T_FINAL:
 567              case T_ABSTRACT:
 568              case T_CLASS:
 569              case T_INTERFACE:
 570              case T_TRAIT:
 571                  $infos[$infoIndex] = [
 572                      'type'        => $tokenType === T_FUNCTION ? 'function' : 'class',
 573                      'tokenStart'  => $MACRO_TOKEN_LOGICAL_START_INDEX(),
 574                      'tokenEnd'    => null,
 575                      'lineStart'   => $tokens[$tokenIndex][2],
 576                      'lineEnd'     => null,
 577                      'namespace'   => $namespace,
 578                      'uses'        => $this->getUsesNoScan($namespace),
 579                      'name'        => null,
 580                      'shortName'   => null,
 581                  ];
 582  
 583                  $classBraceCount = 0;
 584  
 585                  // start processing with current token
 586  
 587                  SCANNER_CLASS_TOP:
 588  
 589                  // process the name
 590                  if ($infos[$infoIndex]['shortName'] == ''
 591                      && (($tokenType === T_CLASS
 592                              || $tokenType === T_INTERFACE
 593                              || $tokenType === T_TRAIT)
 594                          && $infos[$infoIndex]['type'] === 'class'
 595                          || ($tokenType === T_FUNCTION && $infos[$infoIndex]['type'] === 'function'))
 596                  ) {
 597                      $infos[$infoIndex]['shortName'] = is_array($tokens[$tokenIndex + 2])
 598                          ? $tokens[$tokenIndex + 2][1]
 599                          : $tokens[$tokenIndex + 2];
 600                      $infos[$infoIndex]['name']      = ($namespace !== null
 601                          ? $namespace . '\\'
 602                          : '') . $infos[$infoIndex]['shortName'];
 603                  }
 604  
 605                  if ($tokenType === null) {
 606                      if ($tokenContent == '{') {
 607                          $classBraceCount++;
 608                      }
 609                      if ($tokenContent == '}') {
 610                          $classBraceCount--;
 611                          if ($classBraceCount === 0) {
 612                              goto SCANNER_CLASS_END;
 613                          }
 614                      }
 615                  }
 616  
 617                  SCANNER_CLASS_CONTINUE:
 618  
 619                  if ($MACRO_TOKEN_ADVANCE() === false) {
 620                      goto SCANNER_END;
 621                  }
 622                  goto SCANNER_CLASS_TOP;
 623  
 624                  SCANNER_CLASS_END:
 625  
 626                  $MACRO_INFO_ADVANCE();
 627                  goto SCANNER_CONTINUE;
 628                  // goto no break needed
 629          }
 630  
 631          SCANNER_CONTINUE:
 632  
 633          if ($MACRO_TOKEN_ADVANCE() === false) {
 634              goto SCANNER_END;
 635          }
 636          goto SCANNER_TOP;
 637  
 638          SCANNER_END:
 639  
 640          /**
 641           * END FINITE STATE MACHINE FOR SCANNING TOKENS
 642           */
 643          $this->isScanned = true;
 644      }
 645  
 646      /**
 647       * Check for namespace
 648       *
 649       * @param string $namespace
 650       * @return bool
 651       */
 652      public function hasNamespace($namespace)
 653      {
 654          $this->scan();
 655  
 656          foreach ($this->infos as $info) {
 657              if ($info['type'] == 'namespace' && $info['namespace'] == $namespace) {
 658                  return true;
 659              }
 660          }
 661          return false;
 662      }
 663  
 664      /**
 665       * @param  string $namespace
 666       * @return null|array
 667       * @throws Exception\InvalidArgumentException
 668       */
 669      protected function getUsesNoScan($namespace)
 670      {
 671          $namespaces = [];
 672          foreach ($this->infos as $info) {
 673              if ($info['type'] == 'namespace') {
 674                  $namespaces[] = $info['namespace'];
 675              }
 676          }
 677  
 678          if ($namespace === null) {
 679              $namespace = array_shift($namespaces);
 680          } elseif (! is_string($namespace)) {
 681              throw new Exception\InvalidArgumentException('Invalid namespace provided');
 682          } elseif (! in_array($namespace, $namespaces)) {
 683              return;
 684          }
 685  
 686          $uses = [];
 687          foreach ($this->infos as $info) {
 688              if ($info['type'] !== 'use') {
 689                  continue;
 690              }
 691              foreach ($info['statements'] as $statement) {
 692                  if ($info['namespace'] == $namespace) {
 693                      $uses[] = $statement;
 694                  }
 695              }
 696          }
 697  
 698          return $uses;
 699      }
 700  }


Generated: Sun Jun 23 12:25:44 2024 Cross-referenced by PHPXref 0.7.1