1<?php 2/* 3 * This file is part of code-unit-reverse-lookup. 4 * 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace SebastianBergmann\CodeUnitReverseLookup; 12 13/** 14 * @since Class available since Release 1.0.0 15 */ 16class Wizard 17{ 18 /** 19 * @var array 20 */ 21 private $lookupTable = []; 22 23 /** 24 * @var array 25 */ 26 private $processedClasses = []; 27 28 /** 29 * @var array 30 */ 31 private $processedFunctions = []; 32 33 /** 34 * @param string $filename 35 * @param int $lineNumber 36 * 37 * @return string 38 */ 39 public function lookup($filename, $lineNumber) 40 { 41 if (!isset($this->lookupTable[$filename][$lineNumber])) { 42 $this->updateLookupTable(); 43 } 44 45 if (isset($this->lookupTable[$filename][$lineNumber])) { 46 return $this->lookupTable[$filename][$lineNumber]; 47 } else { 48 return $filename . ':' . $lineNumber; 49 } 50 } 51 52 private function updateLookupTable() 53 { 54 $this->processClassesAndTraits(); 55 $this->processFunctions(); 56 } 57 58 private function processClassesAndTraits() 59 { 60 foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) { 61 if (isset($this->processedClasses[$classOrTrait])) { 62 continue; 63 } 64 65 $reflector = new \ReflectionClass($classOrTrait); 66 67 foreach ($reflector->getMethods() as $method) { 68 $this->processFunctionOrMethod($method); 69 } 70 71 $this->processedClasses[$classOrTrait] = true; 72 } 73 } 74 75 private function processFunctions() 76 { 77 foreach (get_defined_functions()['user'] as $function) { 78 if (isset($this->processedFunctions[$function])) { 79 continue; 80 } 81 82 $this->processFunctionOrMethod(new \ReflectionFunction($function)); 83 84 $this->processedFunctions[$function] = true; 85 } 86 } 87 88 /** 89 * @param \ReflectionFunctionAbstract $functionOrMethod 90 */ 91 private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod) 92 { 93 if ($functionOrMethod->isInternal()) { 94 return; 95 } 96 97 $name = $functionOrMethod->getName(); 98 99 if ($functionOrMethod instanceof \ReflectionMethod) { 100 $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; 101 } 102 103 if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { 104 $this->lookupTable[$functionOrMethod->getFileName()] = []; 105 } 106 107 foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { 108 $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; 109 } 110 } 111} 112