| Total Complexity | 108 |
| Total Lines | 740 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like ReflectionFile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ReflectionFile, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 21 | final class ReflectionFile |
||
| 22 | { |
||
| 23 | /** |
||
| 24 | * Namespace separator. |
||
| 25 | */ |
||
| 26 | public const NS_SEPARATOR = '\\'; |
||
| 27 | |||
| 28 | /** |
||
| 29 | * Constants for convenience. |
||
| 30 | */ |
||
| 31 | public const TOKEN_TYPE = Tokenizer::TYPE; |
||
| 32 | public const TOKEN_CODE = Tokenizer::CODE; |
||
| 33 | public const TOKEN_LINE = Tokenizer::LINE; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Opening and closing token ids. |
||
| 37 | */ |
||
| 38 | public const O_TOKEN = 0; |
||
| 39 | public const C_TOKEN = 1; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Namespace uses. |
||
| 43 | */ |
||
| 44 | public const N_USES = 2; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * Set of tokens required to detect classes, traits, interfaces and function declarations. We |
||
| 48 | * don't need any other token for that. |
||
| 49 | * |
||
| 50 | * @var array |
||
| 51 | */ |
||
| 52 | private static $processTokens = [ |
||
| 53 | '{', |
||
| 54 | '}', |
||
| 55 | ';', |
||
| 56 | T_PAAMAYIM_NEKUDOTAYIM, |
||
| 57 | T_NAMESPACE, |
||
| 58 | T_STRING, |
||
| 59 | T_CLASS, |
||
| 60 | T_INTERFACE, |
||
| 61 | T_TRAIT, |
||
| 62 | T_FUNCTION, |
||
| 63 | T_NS_SEPARATOR, |
||
| 64 | T_INCLUDE, |
||
| 65 | T_INCLUDE_ONCE, |
||
| 66 | T_REQUIRE, |
||
| 67 | T_REQUIRE_ONCE, |
||
| 68 | T_USE, |
||
| 69 | T_AS, |
||
| 70 | ]; |
||
| 71 | |||
| 72 | /** @var string */ |
||
| 73 | private $filename = ''; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Parsed tokens array. |
||
| 77 | * |
||
| 78 | * @internal |
||
| 79 | * @var array |
||
| 80 | */ |
||
| 81 | private $tokens = []; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * Total tokens count. |
||
| 85 | * |
||
| 86 | * @internal |
||
| 87 | * @var int |
||
| 88 | */ |
||
| 89 | private $countTokens = 0; |
||
| 90 | |||
| 91 | /** |
||
| 92 | * Indicator that file has external includes. |
||
| 93 | * |
||
| 94 | * @internal |
||
| 95 | * @var bool |
||
| 96 | */ |
||
| 97 | private $hasIncludes = false; |
||
| 98 | |||
| 99 | /** |
||
| 100 | * Namespaces used in file and their token positions. |
||
| 101 | * |
||
| 102 | * @internal |
||
| 103 | * @var array |
||
| 104 | */ |
||
| 105 | private $namespaces = []; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Declarations of classes, interfaces and traits. |
||
| 109 | * |
||
| 110 | * @internal |
||
| 111 | * @var array |
||
| 112 | */ |
||
| 113 | private $declarations = []; |
||
| 114 | |||
| 115 | /** |
||
| 116 | * Declarations of new functions. |
||
| 117 | * |
||
| 118 | * @internal |
||
| 119 | * @var array |
||
| 120 | */ |
||
| 121 | private $functions = []; |
||
| 122 | |||
| 123 | /** |
||
| 124 | * Every found method/function invocation. |
||
| 125 | * |
||
| 126 | * @internal |
||
| 127 | * @var ReflectionInvocation[] |
||
| 128 | */ |
||
| 129 | private $invocations = []; |
||
| 130 | |||
| 131 | /** |
||
| 132 | * @param string $filename |
||
| 133 | */ |
||
| 134 | public function __construct(string $filename) |
||
| 135 | { |
||
| 136 | $this->filename = $filename; |
||
| 137 | $this->tokens = Tokenizer::getTokens($filename); |
||
| 138 | $this->countTokens = count($this->tokens); |
||
| 139 | |||
| 140 | //Looking for declarations |
||
| 141 | $this->locateDeclarations(); |
||
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Filename. |
||
| 146 | * |
||
| 147 | * @return string |
||
| 148 | */ |
||
| 149 | public function getFilename(): string |
||
| 150 | { |
||
| 151 | return $this->filename; |
||
| 152 | } |
||
| 153 | |||
| 154 | /** |
||
| 155 | * List of declared function names |
||
| 156 | * |
||
| 157 | * @return array |
||
| 158 | */ |
||
| 159 | public function getFunctions(): array |
||
| 160 | { |
||
| 161 | return array_keys($this->functions); |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * List of declared class names |
||
| 166 | * |
||
| 167 | * @return array |
||
| 168 | */ |
||
| 169 | public function getClasses(): array |
||
| 170 | { |
||
| 171 | if (!isset($this->declarations['T_CLASS'])) { |
||
| 172 | return []; |
||
| 173 | } |
||
| 174 | |||
| 175 | return array_keys($this->declarations['T_CLASS']); |
||
| 176 | } |
||
| 177 | |||
| 178 | /** |
||
| 179 | * List of declared trait names |
||
| 180 | * |
||
| 181 | * @return array |
||
| 182 | */ |
||
| 183 | public function getTraits(): array |
||
| 184 | { |
||
| 185 | if (!isset($this->declarations['T_TRAIT'])) { |
||
| 186 | return []; |
||
| 187 | } |
||
| 188 | |||
| 189 | return array_keys($this->declarations['T_TRAIT']); |
||
| 190 | } |
||
| 191 | |||
| 192 | /** |
||
| 193 | * List of declared interface names |
||
| 194 | * |
||
| 195 | * @return array |
||
| 196 | */ |
||
| 197 | public function getInterfaces(): array |
||
| 198 | { |
||
| 199 | if (!isset($this->declarations['T_INTERFACE'])) { |
||
| 200 | return []; |
||
| 201 | } |
||
| 202 | |||
| 203 | return array_keys($this->declarations['T_INTERFACE']); |
||
| 204 | } |
||
| 205 | |||
| 206 | /** |
||
| 207 | * Get list of tokens associated with given file. |
||
| 208 | * |
||
| 209 | * @return array |
||
| 210 | */ |
||
| 211 | public function getTokens(): array |
||
| 212 | { |
||
| 213 | return $this->tokens; |
||
| 214 | } |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Indication that file contains require/include statements |
||
| 218 | * |
||
| 219 | * @return bool |
||
| 220 | */ |
||
| 221 | public function hasIncludes(): bool |
||
| 222 | { |
||
| 223 | return $this->hasIncludes; |
||
| 224 | } |
||
| 225 | |||
| 226 | /** |
||
| 227 | * Locate and return list of every method or function call in specified file. Only static and |
||
| 228 | * $this calls will be indexed |
||
| 229 | * |
||
| 230 | * @return ReflectionInvocation[] |
||
| 231 | */ |
||
| 232 | public function getInvocations(): array |
||
| 233 | { |
||
| 234 | if (empty($this->invocations)) { |
||
| 235 | $this->locateInvocations($this->getTokens()); |
||
| 236 | } |
||
| 237 | |||
| 238 | return $this->invocations; |
||
| 239 | } |
||
| 240 | |||
| 241 | /** |
||
| 242 | * Export found declaration as array for caching purposes. |
||
| 243 | * |
||
| 244 | * @return array |
||
| 245 | */ |
||
| 246 | public function exportSchema(): array |
||
| 247 | { |
||
| 248 | return [$this->hasIncludes, $this->declarations, $this->functions, $this->namespaces]; |
||
| 249 | } |
||
| 250 | |||
| 251 | /** |
||
| 252 | * Import cached reflection schema. |
||
| 253 | * |
||
| 254 | * @param array $cache |
||
| 255 | */ |
||
| 256 | protected function importSchema(array $cache) |
||
| 259 | } |
||
| 260 | |||
| 261 | /** |
||
| 262 | * Locate every class, interface, trait or function definition. |
||
| 263 | */ |
||
| 264 | protected function locateDeclarations() |
||
| 265 | { |
||
| 266 | foreach ($this->getTokens() as $tokenID => $token) { |
||
| 267 | if (!in_array($token[self::TOKEN_TYPE], self::$processTokens)) { |
||
| 268 | continue; |
||
| 269 | } |
||
| 270 | |||
| 271 | switch ($token[self::TOKEN_TYPE]) { |
||
| 272 | case T_NAMESPACE: |
||
| 273 | $this->registerNamespace($tokenID); |
||
| 274 | break; |
||
| 275 | |||
| 276 | case T_USE: |
||
| 277 | $this->registerUse($tokenID); |
||
| 278 | break; |
||
| 279 | |||
| 280 | case T_FUNCTION: |
||
| 281 | $this->registerFunction($tokenID); |
||
| 282 | break; |
||
| 283 | |||
| 284 | case T_CLASS: |
||
| 285 | case T_TRAIT: |
||
| 286 | case T_INTERFACE: |
||
| 287 | if ($this->isClassNameConst($tokenID)) { |
||
| 288 | //PHP5.5 ClassName::class constant |
||
| 289 | continue 2; |
||
| 290 | } |
||
| 291 | |||
| 292 | $this->registerDeclaration($tokenID, $token[self::TOKEN_TYPE]); |
||
| 293 | break; |
||
| 294 | |||
| 295 | case T_INCLUDE: |
||
| 296 | case T_INCLUDE_ONCE: |
||
| 297 | case T_REQUIRE: |
||
| 298 | case T_REQUIRE_ONCE: |
||
| 299 | $this->hasIncludes = true; |
||
| 300 | } |
||
| 301 | } |
||
| 302 | |||
| 303 | //Dropping empty namespace |
||
| 304 | if (isset($this->namespaces[''])) { |
||
| 305 | $this->namespaces['\\'] = $this->namespaces['']; |
||
| 306 | unset($this->namespaces['']); |
||
| 307 | } |
||
| 308 | } |
||
| 309 | |||
| 310 | /** |
||
| 311 | * Handle namespace declaration. |
||
| 312 | * |
||
| 313 | * @param int $tokenID |
||
| 314 | */ |
||
| 315 | private function registerNamespace(int $tokenID) |
||
| 316 | { |
||
| 317 | $namespace = ''; |
||
| 318 | $localID = $tokenID + 1; |
||
| 319 | |||
| 320 | do { |
||
| 321 | $token = $this->tokens[$localID++]; |
||
| 322 | if ($token[self::TOKEN_CODE] == '{') { |
||
| 323 | break; |
||
| 324 | } |
||
| 325 | |||
| 326 | $namespace .= $token[self::TOKEN_CODE]; |
||
| 327 | } while ( |
||
| 328 | isset($this->tokens[$localID]) |
||
| 329 | && $this->tokens[$localID][self::TOKEN_CODE] != '{' |
||
| 330 | && $this->tokens[$localID][self::TOKEN_CODE] != ';' |
||
| 331 | ); |
||
| 332 | |||
| 333 | //Whitespaces |
||
| 334 | $namespace = trim($namespace); |
||
| 335 | |||
| 336 | $uses = []; |
||
| 337 | if (isset($this->namespaces[$namespace])) { |
||
| 338 | $uses = $this->namespaces[$namespace]; |
||
| 339 | } |
||
| 340 | |||
| 341 | if ($this->tokens[$localID][self::TOKEN_CODE] == ';') { |
||
| 342 | $endingID = count($this->tokens) - 1; |
||
| 343 | } else { |
||
| 344 | $endingID = $this->endingToken($tokenID); |
||
| 345 | } |
||
| 346 | |||
| 347 | $this->namespaces[$namespace] = [ |
||
| 348 | self::O_TOKEN => $tokenID, |
||
| 349 | self::C_TOKEN => $endingID, |
||
| 350 | self::N_USES => $uses, |
||
| 351 | ]; |
||
| 352 | } |
||
| 353 | |||
| 354 | /** |
||
| 355 | * Handle use (import class from another namespace). |
||
| 356 | * |
||
| 357 | * @param int $tokenID |
||
| 358 | */ |
||
| 359 | private function registerUse(int $tokenID) |
||
| 384 | } |
||
| 385 | |||
| 386 | /** |
||
| 387 | * Handle function declaration (function creation). |
||
| 388 | * |
||
| 389 | * @param int $tokenID |
||
| 390 | */ |
||
| 391 | private function registerFunction(int $tokenID) |
||
| 392 | { |
||
| 393 | foreach ($this->declarations as $declarations) { |
||
| 394 | foreach ($declarations as $location) { |
||
| 395 | if ($tokenID >= $location[self::O_TOKEN] && $tokenID <= $location[self::C_TOKEN]) { |
||
| 396 | //We are inside class, function is method |
||
| 397 | return; |
||
| 398 | } |
||
| 399 | } |
||
| 400 | } |
||
| 401 | |||
| 402 | $localID = $tokenID + 1; |
||
| 403 | while ($this->tokens[$localID][self::TOKEN_TYPE] != T_STRING) { |
||
| 404 | //Fetching function name |
||
| 405 | ++$localID; |
||
| 406 | } |
||
| 407 | |||
| 408 | $name = $this->tokens[$localID][self::TOKEN_CODE]; |
||
| 409 | if (!empty($namespace = $this->activeNamespace($tokenID))) { |
||
| 410 | $name = $namespace . self::NS_SEPARATOR . $name; |
||
| 411 | } |
||
| 412 | |||
| 413 | $this->functions[$name] = [ |
||
| 414 | self::O_TOKEN => $tokenID, |
||
| 415 | self::C_TOKEN => $this->endingToken($tokenID), |
||
| 416 | ]; |
||
| 417 | } |
||
| 418 | |||
| 419 | /** |
||
| 420 | * Handle declaration of class, trait of interface. Declaration will be stored under it's token |
||
| 421 | * type in declarations array. |
||
| 422 | * |
||
| 423 | * @param int $tokenID |
||
| 424 | * @param int $tokenType |
||
| 425 | */ |
||
| 426 | private function registerDeclaration(int $tokenID, int $tokenType) |
||
| 427 | { |
||
| 428 | $localID = $tokenID + 1; |
||
| 429 | while ($this->tokens[$localID][self::TOKEN_TYPE] != T_STRING) { |
||
| 430 | ++$localID; |
||
| 431 | } |
||
| 432 | |||
| 433 | $name = $this->tokens[$localID][self::TOKEN_CODE]; |
||
| 434 | if (!empty($namespace = $this->activeNamespace($tokenID))) { |
||
| 435 | $name = $namespace . self::NS_SEPARATOR . $name; |
||
| 436 | } |
||
| 437 | |||
| 438 | $this->declarations[token_name($tokenType)][$name] = [ |
||
| 439 | self::O_TOKEN => $tokenID, |
||
| 440 | self::C_TOKEN => $this->endingToken($tokenID), |
||
| 441 | ]; |
||
| 442 | } |
||
| 443 | |||
| 444 | /** |
||
| 445 | * Check if token ID represents `ClassName::class` constant statement. |
||
| 446 | * |
||
| 447 | * @param int $tokenID |
||
| 448 | * |
||
| 449 | * @return bool |
||
| 450 | */ |
||
| 451 | private function isClassNameConst(int $tokenID): bool |
||
| 452 | { |
||
| 453 | return $this->tokens[$tokenID][self::TOKEN_TYPE] == T_CLASS |
||
| 454 | && isset($this->tokens[$tokenID - 1]) |
||
| 455 | && $this->tokens[$tokenID - 1][self::TOKEN_TYPE] == T_PAAMAYIM_NEKUDOTAYIM; |
||
| 456 | } |
||
| 457 | |||
| 458 | /** |
||
| 459 | * Locate every function or static method call (including $this calls). |
||
| 460 | * |
||
| 461 | * This is pretty old code, potentially to be improved using AST. |
||
| 462 | * |
||
| 463 | * @param array $tokens |
||
| 464 | * @param int $invocationLevel |
||
| 465 | */ |
||
| 466 | private function locateInvocations(array $tokens, int $invocationLevel = 0) |
||
| 467 | { |
||
| 468 | //Multiple "(" and ")" statements nested. |
||
| 469 | $level = 0; |
||
| 470 | |||
| 471 | //Skip all tokens until next function |
||
| 472 | $ignore = false; |
||
| 473 | |||
| 474 | //Were function was found |
||
| 475 | $invocationTID = 0; |
||
| 476 | |||
| 477 | //Parsed arguments and their first token id |
||
| 478 | $arguments = []; |
||
| 479 | $argumentsTID = false; |
||
| 480 | |||
| 481 | //Tokens used to re-enable token detection |
||
| 482 | $stopTokens = [T_STRING, T_WHITESPACE, T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_NS_SEPARATOR]; |
||
| 483 | foreach ($tokens as $tokenID => $token) { |
||
| 484 | $tokenType = $token[self::TOKEN_TYPE]; |
||
| 485 | |||
| 486 | //We are not indexing function declarations or functions called from $objects. |
||
| 487 | if (in_array($tokenType, [T_FUNCTION, T_OBJECT_OPERATOR, T_NEW])) { |
||
| 488 | if ( |
||
| 489 | empty($argumentsTID) |
||
| 490 | && ( |
||
| 491 | empty($invocationTID) |
||
| 492 | || $this->getSource($invocationTID, $tokenID - 1) != '$this' |
||
| 493 | ) |
||
| 494 | ) { |
||
| 495 | //Not a call, function declaration, or object method |
||
| 496 | $ignore = true; |
||
| 497 | continue; |
||
| 498 | } |
||
| 499 | } elseif ($ignore) { |
||
| 500 | if (!in_array($tokenType, $stopTokens)) { |
||
| 501 | //Returning to search |
||
| 502 | $ignore = false; |
||
| 503 | } |
||
| 504 | continue; |
||
| 505 | } |
||
| 506 | |||
| 507 | //We are inside function, and there is "(", indexing arguments. |
||
| 508 | if (!empty($invocationTID) && ($tokenType == '(' || $tokenType == '[')) { |
||
| 509 | if (empty($argumentsTID)) { |
||
| 510 | $argumentsTID = $tokenID; |
||
| 511 | } |
||
| 512 | |||
| 513 | ++$level; |
||
| 514 | if ($level != 1) { |
||
| 515 | //Not arguments beginning, but arguments part |
||
| 516 | $arguments[$tokenID] = $token; |
||
| 517 | } |
||
| 518 | |||
| 519 | continue; |
||
| 520 | } |
||
| 521 | |||
| 522 | //We are inside function arguments and ")" met. |
||
| 523 | if (!empty($invocationTID) && ($tokenType == ')' || $tokenType == ']')) { |
||
| 524 | --$level; |
||
| 525 | if ($level == -1) { |
||
| 526 | $invocationTID = false; |
||
| 527 | $level = 0; |
||
| 528 | continue; |
||
| 529 | } |
||
| 530 | |||
| 531 | //Function fully indexed, we can process it now. |
||
| 532 | if ($level == 0) { |
||
| 533 | $this->registerInvocation( |
||
| 534 | $invocationTID, |
||
| 535 | $argumentsTID, |
||
| 536 | $tokenID, |
||
| 537 | $arguments, |
||
| 538 | $invocationLevel |
||
| 539 | ); |
||
| 540 | |||
| 541 | //Closing search |
||
| 542 | $arguments = []; |
||
| 543 | $argumentsTID = $invocationTID = false; |
||
| 544 | } else { |
||
| 545 | //Not arguments beginning, but arguments part |
||
| 546 | $arguments[$tokenID] = $token; |
||
| 547 | } |
||
| 548 | |||
| 549 | continue; |
||
| 550 | } |
||
| 551 | |||
| 552 | //Still inside arguments. |
||
| 553 | if (!empty($invocationTID) && !empty($level)) { |
||
| 554 | $arguments[$tokenID] = $token; |
||
| 555 | continue; |
||
| 556 | } |
||
| 557 | |||
| 558 | //Nothing valuable to remember, will be parsed later. |
||
| 559 | if (!empty($invocationTID) && in_array($tokenType, $stopTokens)) { |
||
| 560 | continue; |
||
| 561 | } |
||
| 562 | |||
| 563 | //Seems like we found function/method call |
||
| 564 | if ( |
||
| 565 | $tokenType == T_STRING |
||
| 566 | || $tokenType == T_STATIC |
||
| 567 | || $tokenType == T_NS_SEPARATOR |
||
| 568 | || ($tokenType == T_VARIABLE && $token[self::TOKEN_CODE] == '$this') |
||
| 569 | ) { |
||
| 570 | $invocationTID = $tokenID; |
||
| 571 | $level = 0; |
||
| 572 | |||
| 573 | $argumentsTID = false; |
||
| 574 | continue; |
||
| 575 | } |
||
| 576 | |||
| 577 | //Returning to search |
||
| 578 | $invocationTID = false; |
||
| 579 | $arguments = []; |
||
| 580 | } |
||
| 581 | } |
||
| 582 | |||
| 583 | /** |
||
| 584 | * Registering invocation. |
||
| 585 | * |
||
| 586 | * @param int $invocationID |
||
| 587 | * @param int $argumentsID |
||
| 588 | * @param int $endID |
||
| 589 | * @param array $arguments |
||
| 590 | * @param int $invocationLevel |
||
| 591 | */ |
||
| 592 | private function registerInvocation( |
||
| 593 | int $invocationID, |
||
| 594 | int $argumentsID, |
||
| 595 | int $endID, |
||
| 596 | array $arguments, |
||
| 597 | int $invocationLevel |
||
| 598 | ) { |
||
| 599 | //Nested invocations |
||
| 600 | $this->locateInvocations($arguments, $invocationLevel + 1); |
||
| 601 | |||
| 602 | list($class, $operator, $name) = $this->fetchContext($invocationID, $argumentsID); |
||
| 603 | |||
| 604 | if (!empty($operator) && empty($class)) { |
||
| 605 | //Non detectable |
||
| 606 | return; |
||
| 607 | } |
||
| 608 | |||
| 609 | $this->invocations[] = new ReflectionInvocation( |
||
| 610 | $this->filename, |
||
| 611 | $this->lineNumber($invocationID), |
||
| 612 | $class, |
||
| 613 | $operator, |
||
| 614 | $name, |
||
| 615 | ReflectionArgument::locateArguments($arguments), |
||
| 616 | $this->getSource($invocationID, $endID), |
||
| 617 | $invocationLevel |
||
| 618 | ); |
||
| 619 | } |
||
| 620 | |||
| 621 | /** |
||
| 622 | * Fetching invocation context. |
||
| 623 | * |
||
| 624 | * @param int $invocationTID |
||
| 625 | * @param int $argumentsTID |
||
| 626 | * |
||
| 627 | * @return array |
||
| 628 | */ |
||
| 629 | private function fetchContext(int $invocationTID, int $argumentsTID): array |
||
| 630 | { |
||
| 631 | $class = $operator = ''; |
||
| 632 | $name = trim($this->getSource($invocationTID, $argumentsTID), '( '); |
||
| 633 | |||
| 634 | //Let's try to fetch all information we need |
||
| 635 | if (strpos($name, '->') !== false) { |
||
| 636 | $operator = '->'; |
||
| 637 | } elseif (strpos($name, '::') !== false) { |
||
| 638 | $operator = '::'; |
||
| 639 | } |
||
| 640 | |||
| 641 | if (!empty($operator)) { |
||
| 642 | list($class, $name) = explode($operator, $name); |
||
| 643 | |||
| 644 | //We now have to clarify class name |
||
| 645 | if (in_array($class, ['self', 'static', '$this'])) { |
||
| 646 | $class = $this->activeDeclaration($invocationTID); |
||
| 647 | } |
||
| 648 | } |
||
| 649 | |||
| 650 | return [$class, $operator, $name]; |
||
| 651 | } |
||
| 652 | |||
| 653 | /** |
||
| 654 | * Get declaration which is active in given token position. |
||
| 655 | * |
||
| 656 | * @param int $tokenID |
||
| 657 | * |
||
| 658 | * @return string|null |
||
| 659 | */ |
||
| 660 | private function activeDeclaration(int $tokenID): string |
||
| 672 | } |
||
| 673 | |||
| 674 | /** |
||
| 675 | * Get namespace name active at specified token position. |
||
| 676 | * |
||
| 677 | * @param int $tokenID |
||
| 678 | * |
||
| 679 | * @return string |
||
| 680 | */ |
||
| 681 | private function activeNamespace(int $tokenID): string |
||
| 682 | { |
||
| 683 | foreach ($this->namespaces as $namespace => $position) { |
||
| 684 | if ($tokenID >= $position[self::O_TOKEN] && $tokenID <= $position[self::C_TOKEN]) { |
||
| 685 | return $namespace; |
||
| 686 | } |
||
| 687 | } |
||
| 688 | |||
| 689 | //Seems like no namespace declaration |
||
| 690 | $this->namespaces[''] = [ |
||
| 691 | self::O_TOKEN => 0, |
||
| 692 | self::C_TOKEN => count($this->tokens), |
||
| 693 | self::N_USES => [], |
||
| 694 | ]; |
||
| 695 | |||
| 696 | return ''; |
||
| 697 | } |
||
| 698 | |||
| 699 | /** |
||
| 700 | * Find token ID of ending brace. |
||
| 701 | * |
||
| 702 | * @param int $tokenID |
||
| 703 | * |
||
| 704 | * @return int |
||
| 705 | */ |
||
| 706 | private function endingToken(int $tokenID): int |
||
| 707 | { |
||
| 708 | $level = null; |
||
| 709 | for ($localID = $tokenID; $localID < $this->countTokens; ++$localID) { |
||
| 710 | $token = $this->tokens[$localID]; |
||
| 711 | if ($token[self::TOKEN_CODE] == '{') { |
||
| 712 | ++$level; |
||
| 713 | continue; |
||
| 714 | } |
||
| 715 | |||
| 716 | if ($token[self::TOKEN_CODE] == '}') { |
||
| 717 | --$level; |
||
| 718 | } |
||
| 719 | |||
| 720 | if ($level === 0) { |
||
| 721 | break; |
||
| 722 | } |
||
| 723 | } |
||
| 724 | |||
| 725 | return $localID; |
||
| 726 | } |
||
| 727 | |||
| 728 | /** |
||
| 729 | * Get line number associated with token. |
||
| 730 | * |
||
| 731 | * @param int $tokenID |
||
| 732 | * |
||
| 733 | * @return int |
||
| 734 | */ |
||
| 735 | private function lineNumber(int $tokenID): int |
||
| 736 | { |
||
| 737 | while (empty($this->tokens[$tokenID][self::TOKEN_LINE])) { |
||
| 738 | --$tokenID; |
||
| 739 | } |
||
| 740 | |||
| 741 | return $this->tokens[$tokenID][self::TOKEN_LINE]; |
||
| 742 | } |
||
| 743 | |||
| 744 | /** |
||
| 745 | * Get src located between two tokens. |
||
| 746 | * |
||
| 747 | * @param int $startID |
||
| 748 | * @param int $endID |
||
| 749 | * |
||
| 750 | * @return string |
||
| 751 | */ |
||
| 752 | private function getSource(int $startID, int $endID): string |
||
| 761 | } |
||
| 762 | } |
||
| 763 |