[ Index ]

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


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