Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
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. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
TokenArrayScanner.php
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-2015 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 class TokenArrayScanner implements ScannerInterface
017 {
018 /**
019 * @var bool
020 */
021 protected $isScanned = false;
022
023 /**
024 * @var array
025 */
026 protected $tokens = array();
027
028 /**
029 * @var null
030 */
031 protected $docComment = null;
032
033 /**
034 * @var NameInformation
035 */
036 protected $nameInformation = null;
037
038 /**
039 * @var array
040 */
041 protected $infos = array();
042
043 /**
044 * @var AnnotationManager
045 */
046 protected $annotationManager = null;
047
048 /**
049 * @param null|array $tokens
050 * @param null|AnnotationManager $annotationManager
051 */
052 public function __construct($tokens, AnnotationManager $annotationManager = null)
053 {
054 $this->tokens = $tokens;
055 $this->annotationManager = $annotationManager;
056 }
057
058 /**
059 * @return AnnotationManager
060 */
061 public function getAnnotationManager()
062 {
063 return $this->annotationManager;
064 }
065
066 /**
067 * Get doc comment
068 *
069 * @todo Assignment of $this->docComment should probably be done in scan()
070 * and then $this->getDocComment() just retrieves it.
071 *
072 * @return string
073 */
074 public function getDocComment()
075 {
076 foreach ($this->tokens as $token) {
077 $type = $token[0];
078 $value = $token[1];
079 if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
080 continue;
081 } elseif ($type == T_DOC_COMMENT) {
082 $this->docComment = $value;
083
084 return $this->docComment;
085 } else {
086 // Only whitespace is allowed before file docblocks
087 return;
088 }
089 }
090 }
091
092 /**
093 * @return array
094 */
095 public function getNamespaces()
096 {
097 $this->scan();
098
099 $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 }
690