[ Index ] |
PHP Cross Reference of phpBB-3.2.11-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-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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |