Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

TokenArrayScanner.php

Zuletzt modifiziert: 01.05.2026, 11:27 - Dateigröße: 19.32 KiB


 001  <?php
 002  /**
 003   * Zend Framework (http://framework.zend.com/)
 004   *
 005   * @link      http://github.com/zendframework/zf2 for the canonical source repository
 006   * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 007   * @license   http://framework.zend.com/license/new-bsd New BSD License
 008   */
 009   
 010  namespace Zend\Code\Scanner;
 011   
 012  use Zend\Code\Annotation\AnnotationManager;
 013  use Zend\Code\Exception;
 014  use Zend\Code\NameInformation;
 015   
 016  use function array_shift;
 017  use function array_slice;
 018  use function in_array;
 019  use function is_array;
 020  use function is_int;
 021  use function is_string;
 022   
 023  class TokenArrayScanner implements ScannerInterface
 024  {
 025      /**
 026       * @var bool
 027       */
 028      protected $isScanned = false;
 029   
 030      /**
 031       * @var array
 032       */
 033      protected $tokens = [];
 034   
 035      /**
 036       * @var null
 037       */
 038      protected $docComment;
 039   
 040      /**
 041       * @var NameInformation
 042       */
 043      protected $nameInformation;
 044   
 045      /**
 046       * @var array
 047       */
 048      protected $infos = [];
 049   
 050      /**
 051       * @var AnnotationManager
 052       */
 053      protected $annotationManager;
 054   
 055      /**
 056       * @param null|array $tokens
 057       * @param null|AnnotationManager $annotationManager
 058       */
 059      public function __construct($tokens, AnnotationManager $annotationManager = null)
 060      {
 061          $this->tokens            = $tokens;
 062          $this->annotationManager = $annotationManager;
 063      }
 064   
 065      /**
 066       * @return AnnotationManager
 067       */
 068      public function getAnnotationManager()
 069      {
 070          return $this->annotationManager;
 071      }
 072   
 073      /**
 074       * Get doc comment
 075       *
 076       * @todo Assignment of $this->docComment should probably be done in scan()
 077       *       and then $this->getDocComment() just retrieves it.
 078       *
 079       * @return string|null
 080       */
 081      public function getDocComment()
 082      {
 083          foreach ($this->tokens as $token) {
 084              $type    = $token[0];
 085              $value   = $token[1];
 086              if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
 087                  continue;
 088              } elseif ($type == T_DOC_COMMENT) {
 089                  $this->docComment = $value;
 090   
 091                  return $this->docComment;
 092              } else {
 093                  // Only whitespace is allowed before file docblocks
 094                  return;
 095              }
 096          }
 097      }
 098   
 099      /**
 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  }
 701