From 874b4bc6152792248bb71a6a8844aed0e7ca75c9 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 21 Oct 2026 07:55:26 +0200 Subject: [PATCH] [perf] Check method name before type inference in DBAL/ORM method call rules --- bench/cast_doctrine_expr_name_guard_bench.php | 84 +++++++++++++++++++ .../ReplaceFetchAllMethodCallRector.php | 5 ++ .../CastDoctrineExprToStringRector.php | 5 +- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 bench/cast_doctrine_expr_name_guard_bench.php diff --git a/bench/cast_doctrine_expr_name_guard_bench.php b/bench/cast_doctrine_expr_name_guard_bench.php new file mode 100644 index 00000000..91dca451 --- /dev/null +++ b/bench/cast_doctrine_expr_name_guard_bench.php @@ -0,0 +1,84 @@ +create(); +$rectorConfig->boot(); + +/** @var TestingParser $testingParser */ +$testingParser = $rectorConfig->make(TestingParser::class); + +/** @var BetterNodeFinder $betterNodeFinder */ +$betterNodeFinder = $rectorConfig->make(BetterNodeFinder::class); + +$rector = $rectorConfig->make(CastDoctrineExprToStringRector::class); + +$source = "handle{$i}(\$value, {$i});\n"; +} + +$sourceFilePath = tempnam(sys_get_temp_dir(), 'bench_') . '.php'; +file_put_contents($sourceFilePath, $source); + +$stmts = $testingParser->parseFileToDecoratedNodes($sourceFilePath); + +/** @var MethodCall[] $methodCalls */ +$methodCalls = $betterNodeFinder->findInstanceOf($stmts, MethodCall::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, $methodCalls): void { + foreach ($methodCalls as $methodCall) { + $rector->refactor($methodCall); + } +}; + +$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($methodCalls); + +printf('calls: %d, repeats: %d, refactor() calls: %d%s', count($methodCalls), 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/Dbal211/Rector/MethodCall/ReplaceFetchAllMethodCallRector.php b/rules/Dbal211/Rector/MethodCall/ReplaceFetchAllMethodCallRector.php index eab572a6..a89e45a5 100644 --- a/rules/Dbal211/Rector/MethodCall/ReplaceFetchAllMethodCallRector.php +++ b/rules/Dbal211/Rector/MethodCall/ReplaceFetchAllMethodCallRector.php @@ -78,6 +78,11 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { + // cheap name check first - a non-matching call bails before any type inference + if (! $this->isNames($node->name, ['fetchAll', 'fetchArray', 'fetchColumn'])) { + return null; + } + if ($this->isObjectType($node->var, new ObjectType(DoctrineClass::CONNECTION))) { return $this->refactorConnection($node); } diff --git a/rules/Orm30/Rector/MethodCall/CastDoctrineExprToStringRector.php b/rules/Orm30/Rector/MethodCall/CastDoctrineExprToStringRector.php index 70204e2d..e10b395c 100644 --- a/rules/Orm30/Rector/MethodCall/CastDoctrineExprToStringRector.php +++ b/rules/Orm30/Rector/MethodCall/CastDoctrineExprToStringRector.php @@ -81,11 +81,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (! $this->isObjectType($node->var, new ObjectType(DoctrineClass::QUERY_EXPR))) { + // cheap name check first - a non-matching call bails before any type inference + if (! in_array($this->getName($node->name), $this->targetMethods, true)) { return null; } - if (! in_array($this->getName($node->name), $this->targetMethods, true)) { + if (! $this->isObjectType($node->var, new ObjectType(DoctrineClass::QUERY_EXPR))) { return null; }