Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace SebastianBergmann\CodeUnitReverseLookup; Chris@14: Chris@14: /** Chris@14: * @since Class available since Release 1.0.0 Chris@14: */ Chris@14: class Wizard Chris@14: { Chris@14: /** Chris@14: * @var array Chris@14: */ Chris@14: private $lookupTable = []; Chris@14: Chris@14: /** Chris@14: * @var array Chris@14: */ Chris@14: private $processedClasses = []; Chris@14: Chris@14: /** Chris@14: * @var array Chris@14: */ Chris@14: private $processedFunctions = []; Chris@14: Chris@14: /** Chris@14: * @param string $filename Chris@14: * @param int $lineNumber Chris@14: * Chris@14: * @return string Chris@14: */ Chris@14: public function lookup($filename, $lineNumber) Chris@14: { Chris@14: if (!isset($this->lookupTable[$filename][$lineNumber])) { Chris@14: $this->updateLookupTable(); Chris@14: } Chris@14: Chris@14: if (isset($this->lookupTable[$filename][$lineNumber])) { Chris@14: return $this->lookupTable[$filename][$lineNumber]; Chris@14: } else { Chris@14: return $filename . ':' . $lineNumber; Chris@14: } Chris@14: } Chris@14: Chris@14: private function updateLookupTable() Chris@14: { Chris@14: $this->processClassesAndTraits(); Chris@14: $this->processFunctions(); Chris@14: } Chris@14: Chris@14: private function processClassesAndTraits() Chris@14: { Chris@14: foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) { Chris@14: if (isset($this->processedClasses[$classOrTrait])) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: $reflector = new \ReflectionClass($classOrTrait); Chris@14: Chris@14: foreach ($reflector->getMethods() as $method) { Chris@14: $this->processFunctionOrMethod($method); Chris@14: } Chris@14: Chris@14: $this->processedClasses[$classOrTrait] = true; Chris@14: } Chris@14: } Chris@14: Chris@14: private function processFunctions() Chris@14: { Chris@14: foreach (get_defined_functions()['user'] as $function) { Chris@14: if (isset($this->processedFunctions[$function])) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: $this->processFunctionOrMethod(new \ReflectionFunction($function)); Chris@14: Chris@14: $this->processedFunctions[$function] = true; Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param \ReflectionFunctionAbstract $functionOrMethod Chris@14: */ Chris@14: private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod) Chris@14: { Chris@14: if ($functionOrMethod->isInternal()) { Chris@14: return; Chris@14: } Chris@14: Chris@14: $name = $functionOrMethod->getName(); Chris@14: Chris@14: if ($functionOrMethod instanceof \ReflectionMethod) { Chris@14: $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; Chris@14: } Chris@14: Chris@14: if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { Chris@14: $this->lookupTable[$functionOrMethod->getFileName()] = []; Chris@14: } Chris@14: Chris@14: foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { Chris@14: $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; Chris@14: } Chris@14: } Chris@14: }