Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:03 - 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