diff --git a/bench/rename_static_method_bucket_bench.php b/bench/rename_static_method_bucket_bench.php new file mode 100644 index 00000000000..b3df4e4038c --- /dev/null +++ b/bench/rename_static_method_bucket_bench.php @@ -0,0 +1,99 @@ +create(); +$rectorConfig->boot(); + +/** @var TestingParser $testingParser */ +$testingParser = $rectorConfig->make(TestingParser::class); + +/** @var BetterNodeFinder $betterNodeFinder */ +$betterNodeFinder = $rectorConfig->make(BetterNodeFinder::class); + +$rector = $rectorConfig->make(RenameStaticMethodRector::class); + +$staticMethodRenames = []; +for ($i = 0; $i < RENAMES; ++$i) { + $staticMethodRenames[] = new RenameStaticMethod('Bench\\SomeClass' . $i, 'oldMethod' . $i, 'Bench\\NewClass' . $i, 'newMethod' . $i); +} + +$rector->configure($staticMethodRenames); + +$source = "parseFileToDecoratedNodes($sourceFilePath); + +/** @var StaticCall[] $staticCalls */ +$staticCalls = $betterNodeFinder->findInstanceOf($stmts, StaticCall::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, $staticCalls): void { + foreach ($staticCalls as $staticCall) { + $rector->refactor($staticCall); + } +}; + +// 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($staticCalls); + +printf('renames: %d, calls: %d, repeats: %d, refactor() calls: %d%s', RENAMES, count($staticCalls), 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/ClassConstFetch/RenameClassConstFetchRector.php b/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php index fc66396eefe..944da89b043 100644 --- a/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php +++ b/rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php @@ -23,9 +23,9 @@ final class RenameClassConstFetchRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var RenameClassConstFetchInterface[] + * @var array */ - private array $renameClassConstFetches = []; + private array $renameClassConstFetchesByOldConstant = []; public function getRuleDefinition(): RuleDefinition { @@ -70,11 +70,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?ClassConstFetch { - foreach ($this->renameClassConstFetches as $renameClassConstFetch) { - if (! $this->isName($node->name, $renameClassConstFetch->getOldConstant())) { - continue; - } + $constantName = $this->getName($node->name); + if ($constantName === null) { + return null; + } + foreach ($this->renameClassConstFetchesByOldConstant[strtolower($constantName)] ?? [] as $renameClassConstFetch) { if (! $this->isObjectType($node->class, $renameClassConstFetch->getOldObjectType())) { continue; } @@ -98,7 +99,11 @@ public function configure(array $configuration): void { Assert::allIsAOf($configuration, RenameClassConstFetchInterface::class); - $this->renameClassConstFetches = $configuration; + // bucket by lowercased old constant name, matching the case-insensitive isName() lookup + $this->renameClassConstFetchesByOldConstant = []; + foreach ($configuration as $renameClassConstFetch) { + $this->renameClassConstFetchesByOldConstant[strtolower($renameClassConstFetch->getOldConstant())][] = $renameClassConstFetch; + } } private function createClassAndConstFetch(RenameClassAndConstFetch $renameClassAndConstFetch): ClassConstFetch diff --git a/rules/Renaming/Rector/Class_/RenameAttributeRector.php b/rules/Renaming/Rector/Class_/RenameAttributeRector.php index fb3b3cff0a5..772706c274d 100644 --- a/rules/Renaming/Rector/Class_/RenameAttributeRector.php +++ b/rules/Renaming/Rector/Class_/RenameAttributeRector.php @@ -26,9 +26,9 @@ final class RenameAttributeRector extends AbstractRector implements ConfigurableRectorInterface, MinPhpVersionInterface { /** - * @var RenameAttribute[] + * @var array */ - private array $renameAttributes = []; + private array $renameAttributesByOldAttribute = []; public function getRuleDefinition(): RuleDefinition { @@ -97,7 +97,11 @@ public function configure(array $configuration): void { Assert::allIsAOf($configuration, RenameAttribute::class); - $this->renameAttributes = $configuration; + // bucket by lowercased old attribute name (class names are case-insensitive) + $this->renameAttributesByOldAttribute = []; + foreach ($configuration as $renameAttribute) { + $this->renameAttributesByOldAttribute[strtolower($renameAttribute->getOldAttribute())][] = $renameAttribute; + } } public function provideMinPhpVersion(): int @@ -107,7 +111,12 @@ public function provideMinPhpVersion(): int private function matchNewAttributeName(Attribute $attribute): ?string { - foreach ($this->renameAttributes as $renameAttribute) { + $attributeName = $this->getName($attribute->name); + if ($attributeName === null) { + return null; + } + + foreach ($this->renameAttributesByOldAttribute[strtolower($attributeName)] ?? [] as $renameAttribute) { if ($this->isName($attribute->name, $renameAttribute->getOldAttribute())) { return $renameAttribute->getNewAttribute(); } diff --git a/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php b/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php index 6a510df429c..476a93304f3 100644 --- a/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php +++ b/rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php @@ -31,6 +31,11 @@ final class RenamePropertyRector extends AbstractRector implements ConfigurableR */ private array $renamedProperties = []; + /** + * @var array + */ + private array $renamedPropertiesByOldProperty = []; + private bool $hasChanged = false; public function __construct( @@ -86,6 +91,12 @@ public function configure(array $configuration): void { Assert::allIsAOf($configuration, RenameProperty::class); $this->renamedProperties = $configuration; + + // bucket by lowercased old property name for the per-fetch path + $this->renamedPropertiesByOldProperty = []; + foreach ($configuration as $renameProperty) { + $this->renamedPropertiesByOldProperty[strtolower($renameProperty->getOldProperty())][] = $renameProperty; + } } private function renameProperty(ClassLike $classLike, RenameProperty $renameProperty): void @@ -136,12 +147,12 @@ private function isUsingTrait(ClassLike $classLike, string $traitName): bool private function refactorPropertyFetch( PropertyFetch|StaticPropertyFetch $propertyFetch ): null|PropertyFetch|StaticPropertyFetch { - foreach ($this->renamedProperties as $renamedProperty) { - $oldProperty = $renamedProperty->getOldProperty(); - if (! $this->isName($propertyFetch, $oldProperty)) { - continue; - } + $propertyName = $this->getName($propertyFetch); + if ($propertyName === null) { + return null; + } + foreach ($this->renamedPropertiesByOldProperty[strtolower($propertyName)] ?? [] as $renamedProperty) { $varPropertyFetch = $propertyFetch instanceof PropertyFetch ? $propertyFetch->var : $propertyFetch->class; if (! $this->isObjectType($varPropertyFetch, $renamedProperty->getObjectType())) { continue; diff --git a/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php b/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php index fc389679388..e5b619f1b20 100644 --- a/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php +++ b/rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php @@ -21,9 +21,9 @@ final class RenameStaticMethodRector extends AbstractRector implements ConfigurableRectorInterface { /** - * @var RenameStaticMethod[] + * @var array */ - private array $staticMethodRenames = []; + private array $staticMethodRenamesByOldMethod = []; public function getRuleDefinition(): RuleDefinition { @@ -49,11 +49,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - foreach ($this->staticMethodRenames as $staticMethodRename) { - if (! $this->isName($node->name, $staticMethodRename->getOldMethod())) { - continue; - } + $methodName = $this->getName($node->name); + if ($methodName === null) { + return null; + } + foreach ($this->staticMethodRenamesByOldMethod[strtolower($methodName)] ?? [] as $staticMethodRename) { if (! $this->isObjectType($node->class, $staticMethodRename->getOldObjectType())) { continue; } @@ -70,7 +71,12 @@ public function refactor(Node $node): ?Node public function configure(array $configuration): void { Assert::allIsAOf($configuration, RenameStaticMethod::class); - $this->staticMethodRenames = $configuration; + + // bucket by lowercased old method name (method names are case-insensitive) + $this->staticMethodRenamesByOldMethod = []; + foreach ($configuration as $staticMethodRename) { + $this->staticMethodRenamesByOldMethod[strtolower($staticMethodRename->getOldMethod())][] = $staticMethodRename; + } } private function rename(StaticCall $staticCall, RenameStaticMethod $renameStaticMethod): StaticCall