diff --git a/bench/rename_class_const_fetch_lookup_bench.php b/bench/rename_class_const_fetch_lookup_bench.php new file mode 100644 index 00000000000..7e51e783eee --- /dev/null +++ b/bench/rename_class_const_fetch_lookup_bench.php @@ -0,0 +1,100 @@ +create(); +$rectorConfig->boot(); + +/** @var TestingParser $testingParser */ +$testingParser = $rectorConfig->make(TestingParser::class); + +/** @var BetterNodeFinder $betterNodeFinder */ +$betterNodeFinder = $rectorConfig->make(BetterNodeFinder::class); + +$rector = $rectorConfig->make(RenameClassRector::class); + +$configuration = []; +for ($i = 0; $i < RENAMES; ++$i) { + $configuration['Bench\\SomeOldClass' . $i] = 'Bench\\SomeNewClass' . $i; +} + +$rector->configure($configuration); + +$source = "parseFileToDecoratedNodes($sourceFilePath); + +/** @var FullyQualified[] $fullyQualifieds */ +$fullyQualifieds = $betterNodeFinder->findInstanceOf($stmts, FullyQualified::class); + +$cpuSeconds = static function (): float { + $usage = getrusage(); + return $usage['ru_utime.tv_sec'] + $usage['ru_utime.tv_usec'] / 1_000_000 + + $usage['ru_stime.tv_sec'] + $usage['ru_stime.tv_usec'] / 1_000_000; +}; + +$run = static function () use ($rector, $fullyQualifieds): void { + foreach ($fullyQualifieds as $fullyQualified) { + $rector->refactor($fullyQualified); + } +}; + +// warm up autoloading/reflection caches once, excluded from timing +$run(); + +$wallStart = microtime(true); +$cpuStart = $cpuSeconds(); + +for ($repeat = 0; $repeat < REPEAT; ++$repeat) { + $run(); +} + +$cpu = $cpuSeconds() - $cpuStart; +$wall = microtime(true) - $wallStart; + +@unlink($sourceFilePath); + +$totalCalls = REPEAT * count($fullyQualifieds); + +printf('renames: %d, fetches: %d, repeats: %d, refactor() calls: %d%s', RENAMES, count($fullyQualifieds), REPEAT, $totalCalls, PHP_EOL); +printf('CPU: %.4f s (%.3f us/call)%s', $cpu, $cpu / $totalCalls * 1_000_000, PHP_EOL); +printf('wall: %.4f s%s', $wall, PHP_EOL); diff --git a/rules/Renaming/Rector/Name/RenameClassRector.php b/rules/Renaming/Rector/Name/RenameClassRector.php index 12b25abec45..f373fe4cb87 100644 --- a/rules/Renaming/Rector/Name/RenameClassRector.php +++ b/rules/Renaming/Rector/Name/RenameClassRector.php @@ -139,7 +139,8 @@ private function shouldSkipClassConstFetchForMissingConstantName( FullyQualified $fullyQualified, array $oldToNewClasses ): bool { - if (! $this->reflectionProvider->hasClass($fullyQualified->toString())) { + $oldClass = $fullyQualified->toString(); + if (! $this->reflectionProvider->hasClass($oldClass)) { return false; } @@ -149,24 +150,20 @@ private function shouldSkipClassConstFetchForMissingConstantName( return false; } - foreach ($oldToNewClasses as $oldClass => $newClass) { - if (! $this->isName($fullyQualified, $oldClass)) { - continue; - } - - if (! $this->reflectionProvider->hasClass($newClass)) { - continue; - } - - $classReflection = $this->reflectionProvider->getClass($newClass); - $oldClassReflection = $this->reflectionProvider->getClass($oldClass); + // the node name is looked up directly, same as the rename itself does, instead of scanning the whole map + $newClass = $oldToNewClasses[$oldClass] ?? null; + if ($newClass === null) { + return false; + } - if ($oldClassReflection->hasConstant($constFetchName) && ! $classReflection->hasConstant($constFetchName)) { - // should be skipped as new class does not have access to the constant - return true; - } + if (! $this->reflectionProvider->hasClass($newClass)) { + return false; } - return false; + $classReflection = $this->reflectionProvider->getClass($newClass); + $oldClassReflection = $this->reflectionProvider->getClass($oldClass); + + // should be skipped as new class does not have access to the constant + return $oldClassReflection->hasConstant($constFetchName) && ! $classReflection->hasConstant($constFetchName); } }