diff --git a/bench/annotation_to_attribute_generic_tag_bench.php b/bench/annotation_to_attribute_generic_tag_bench.php new file mode 100644 index 00000000000..26cf6072a14 --- /dev/null +++ b/bench/annotation_to_attribute_generic_tag_bench.php @@ -0,0 +1,102 @@ +create(); +$rectorConfig->boot(); + +/** @var TestingParser $testingParser */ +$testingParser = $rectorConfig->make(TestingParser::class); + +/** @var BetterNodeFinder $betterNodeFinder */ +$betterNodeFinder = $rectorConfig->make(BetterNodeFinder::class); + +$rector = $rectorConfig->make(AnnotationToAttributeRector::class); + +$annotationsToAttributes = []; +for ($i = 0; $i < ENTRIES; ++$i) { + $annotationsToAttributes[] = new AnnotationToAttribute('Bench\\SomeAnnotation' . $i); +} + +$rector->configure($annotationsToAttributes); + +$source = "parseFileToDecoratedNodes($sourceFilePath); + +/** @var ClassMethod[] $classMethods */ +$classMethods = $betterNodeFinder->findInstanceOf($stmts, ClassMethod::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, $classMethods): void { + foreach ($classMethods as $classMethod) { + $rector->refactor($classMethod); + } +}; + +// 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($classMethods); + +printf('entries: %d, methods: %d, repeats: %d, refactor() calls: %d%s', ENTRIES, count($classMethods), 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/Php80/Rector/Class_/AnnotationToAttributeRector.php b/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php index f166ac63e68..e5e7c0fb3d8 100644 --- a/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php +++ b/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php @@ -63,6 +63,11 @@ final class AnnotationToAttributeRector extends AbstractRector implements Config */ private array $annotationsToAttributes = []; + /** + * @var array + */ + private array $annotationsToAttributesByTag = []; + public function __construct( private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory, private readonly AttrGroupsFactory $attrGroupsFactory, @@ -178,6 +183,12 @@ public function configure(array $configuration): void Assert::allIsAOf($configuration, AnnotationToAttribute::class); $this->annotationsToAttributes = $this->resolveWithChangedAttributesClass($configuration); + + // bucket by lowercased tag for the generic-tag path + $this->annotationsToAttributesByTag = []; + foreach ($this->annotationsToAttributes as $annotationToAttribute) { + $this->annotationsToAttributesByTag[strtolower($annotationToAttribute->getTag())][] = $annotationToAttribute; + } } public function provideMinPhpVersion(): int @@ -234,12 +245,7 @@ private function processGenericTags(PhpDocInfo $phpDocInfo): array return null; } - foreach ($this->annotationsToAttributes as $annotationToAttribute) { - $desiredTag = $annotationToAttribute->getTag(); - if (strtolower($desiredTag) !== strtolower($tag)) { - continue; - } - + foreach ($this->annotationsToAttributesByTag[strtolower($tag)] ?? [] as $annotationToAttribute) { // make sure the attribute class really exists to avoid error on early upgrade if (! $this->reflectionProvider->hasClass($annotationToAttribute->getAttributeClass())) { continue;