[ Index ] |
PHP Cross Reference of phpBB-3.3.12-deutsch |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Jun 23 12:25:44 2024 | Cross-referenced by PHPXref 0.7.1 |