From f2c7ecb28080d15e7e84a320e3b876dd73323df8 Mon Sep 17 00:00:00 2001 From: Alfred Bez <1001186+alfredbez@users.noreply.github.com> Date: Sun, 18 Oct 2026 07:00:16 +0000 Subject: [PATCH] perf: defer static-to-self scope lookup --- ...onvert_static_to_self_lazy_scope_bench.php | 78 +++++++++++++++++++ .../skip_without_static_access.php.inc | 11 +++ .../Class_/ConvertStaticToSelfRector.php | 44 ++++++----- 3 files changed, 113 insertions(+), 20 deletions(-) create mode 100644 bench/convert_static_to_self_lazy_scope_bench.php create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_without_static_access.php.inc diff --git a/bench/convert_static_to_self_lazy_scope_bench.php b/bench/convert_static_to_self_lazy_scope_bench.php new file mode 100644 index 00000000000..ac89a42d1f0 --- /dev/null +++ b/bench/convert_static_to_self_lazy_scope_bench.php @@ -0,0 +1,78 @@ +create(); +$rectorConfig->boot(); + +/** @var TestingParser $testingParser */ +$testingParser = $rectorConfig->make(TestingParser::class); + +/** @var BetterNodeFinder $betterNodeFinder */ +$betterNodeFinder = $rectorConfig->make(BetterNodeFinder::class); + +$rector = $rectorConfig->make(ConvertStaticToSelfRector::class); + +$source = "parseFileToDecoratedNodes($sourceFilePath); + +/** @var Class_[] $classes */ +$classes = $betterNodeFinder->findInstanceOf($stmts, Class_::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, $classes): void { + foreach ($classes as $class) { + $rector->refactor($class); + } +}; + +$run(); + +$wallStart = microtime(true); +$cpuStart = $cpuSeconds(); + +for ($repeat = 0; $repeat < REPEAT; ++$repeat) { + $run(); +} + +$cpu = $cpuSeconds() - $cpuStart; +$wall = microtime(true) - $wallStart; + +if (is_file($sourceFilePath)) { + unlink($sourceFilePath); +} + +if (is_file($tempBase)) { + unlink($tempBase); +} + +$totalCalls = REPEAT * count($classes); + +printf('classes: %d, repeats: %d, refactor() calls: %d%s', count($classes), 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-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_without_static_access.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_without_static_access.php.inc new file mode 100644 index 00000000000..5acb18c3e3b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_without_static_access.php.inc @@ -0,0 +1,11 @@ +isFinal() || FeatureFlags::treatClassesAsFinal($node); - $scope = ScopeFetcher::fetch($node); - $classReflection = $scope->getClassReflection(); - - if (! $classReflection instanceof ClassReflection) { - return null; - } + $scope = null; + $classReflection = null; $this->traverseNodesWithCallable($node->stmts, function (Node $subNode) use ( &$hasChanged, - $classReflection, + &$classReflection, $isFinal, - $scope, + &$scope, + $node, ): ?Node { if ( ! $subNode instanceof StaticPropertyFetch @@ -98,6 +95,25 @@ public function refactor(Node $node): ?Class_ return null; } + if (! $subNode->class instanceof Name) { + return null; + } + + if (! $this->isName($subNode->class, ObjectReference::STATIC)) { + return null; + } + + if (! $subNode->name instanceof Identifier) { + return null; + } + + $scope ??= ScopeFetcher::fetch($node); + $classReflection ??= $scope->getClassReflection(); + + if (! $classReflection instanceof ClassReflection) { + return null; + } + if ($this->shouldSkip($subNode, $classReflection, $isFinal, $scope)) { return null; } @@ -116,18 +132,6 @@ private function shouldSkip( bool $isFinal, Scope $scope, ): bool { - if (! $node->class instanceof Name) { - return true; - } - - if (! $this->isName($node->class, ObjectReference::STATIC)) { - return true; - } - - if (! $node->name instanceof Identifier) { - return true; - } - $name = (string) $this->getName($node->name); // For final classes, we can safely convert all static:: to self::, even