From 08712b1e76511753e6ab8e15eae323252c0d816a Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 17 Oct 2026 17:11:40 +0200 Subject: [PATCH 1/2] introduce ClassNameImportContext to cache per-file import facts for skip voters --- .../ClassNameImport/AliasUsesResolver.php | 25 ----- .../ClassNameImportContext.php | 104 ++++++++++++++++++ .../AliasClassNameImportSkipVoter.php | 6 +- .../UsesClassNameImportSkipVoter.php | 14 +-- 4 files changed, 114 insertions(+), 35 deletions(-) create mode 100644 rules/CodingStyle/ClassNameImport/ClassNameImportContext.php diff --git a/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php b/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php index 2419c381f58..e514d5f3261 100644 --- a/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php +++ b/rules/CodingStyle/ClassNameImport/AliasUsesResolver.php @@ -4,13 +4,10 @@ namespace Rector\CodingStyle\ClassNameImport; -use PhpParser\Node; use PhpParser\Node\Identifier; use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Use_; use PhpParser\Node\UseItem; -use Rector\PhpParser\Node\FileNode; final readonly class AliasUsesResolver { @@ -19,28 +16,6 @@ public function __construct( ) { } - /** - * @param Stmt[] $stmts - * @return string[] - */ - public function resolveFromNode(Node $node, array $stmts): array - { - if (! $node instanceof Namespace_ && ! $node instanceof FileNode) { - /** @var Namespace_[]|FileNode[] $namespaces */ - $namespaces = array_filter( - $stmts, - static fn (Stmt $stmt): bool => $stmt instanceof Namespace_ || $stmt instanceof FileNode - ); - if (count($namespaces) !== 1) { - return []; - } - - $node = current($namespaces); - } - - return $this->resolveFromStmts($node->stmts); - } - /** * @param Stmt[] $stmts * @return string[] diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportContext.php b/rules/CodingStyle/ClassNameImport/ClassNameImportContext.php new file mode 100644 index 00000000000..7bf3d1562e7 --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportContext.php @@ -0,0 +1,104 @@ + + */ + private array $usedImportTypes = []; + + public function __construct( + private readonly AliasUsesResolver $aliasUsesResolver + ) { + } + + /** + * @return string[] + */ + public function resolveAliasedUses(File $file): array + { + $this->ensureResolved($file); + + return $this->aliasedUses; + } + + /** + * @return array + */ + public function resolveUsedImportTypes(File $file): array + { + $this->ensureResolved($file); + + return $this->usedImportTypes; + } + + private function ensureResolved(File $file): void + { + $newStmtsVersion = $file->getNewStmtsVersion(); + if ($file === $this->cachedFile && $newStmtsVersion === $this->cachedVersion) { + return; + } + + $this->cachedFile = $file; + $this->cachedVersion = $newStmtsVersion; + $this->aliasedUses = $this->resolveAliasedUsesFromFile($file); + $this->usedImportTypes = $this->resolveUsedImportTypesFromFile($file); + } + + /** + * @return string[] + */ + private function resolveAliasedUsesFromFile(File $file): array + { + /** @var Namespace_[]|FileNode[] $namespaces */ + $namespaces = array_filter( + $file->getNewStmts(), + static fn (Stmt $stmt): bool => $stmt instanceof Namespace_ || $stmt instanceof FileNode + ); + + if (count($namespaces) !== 1) { + return []; + } + + $namespace = current($namespaces); + + return $this->aliasUsesResolver->resolveFromStmts($namespace->stmts); + } + + /** + * @return array + */ + private function resolveUsedImportTypesFromFile(File $file): array + { + $fileNode = $file->getFileNode(); + if (! $fileNode instanceof FileNode) { + return []; + } + + return $fileNode->resolveUsedImportTypes(); + } +} diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php index c774a0c4e07..0812f6da40a 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/AliasClassNameImportSkipVoter.php @@ -5,7 +5,7 @@ namespace Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter; use PhpParser\Node; -use Rector\CodingStyle\ClassNameImport\AliasUsesResolver; +use Rector\CodingStyle\ClassNameImport\ClassNameImportContext; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; use Rector\ValueObject\Application\File; @@ -22,13 +22,13 @@ final readonly class AliasClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface { public function __construct( - private AliasUsesResolver $aliasUsesResolver + private ClassNameImportContext $classNameImportContext ) { } public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node): bool { - $aliasedUses = $this->aliasUsesResolver->resolveFromNode($node, $file->getNewStmts()); + $aliasedUses = $this->classNameImportContext->resolveAliasedUses($file); $longNameLowered = strtolower($fullyQualifiedObjectType->getClassName()); $shortNameLowered = $fullyQualifiedObjectType->getShortNameLowered(); diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php index a397a837651..eed696d7a1e 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/UsesClassNameImportSkipVoter.php @@ -5,8 +5,8 @@ namespace Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter; use PhpParser\Node; +use Rector\CodingStyle\ClassNameImport\ClassNameImportContext; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; -use Rector\PhpParser\Node\FileNode; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; use Rector\ValueObject\Application\File; @@ -19,14 +19,14 @@ */ final readonly class UsesClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface { + public function __construct( + private ClassNameImportContext $classNameImportContext + ) { + } + public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node): bool { - $fileNode = $file->getFileNode(); - if (! $fileNode instanceof FileNode) { - return false; - } - - $useImportTypes = $fileNode->resolveUsedImportTypes(); + $useImportTypes = $this->classNameImportContext->resolveUsedImportTypes($file); foreach ($useImportTypes as $useImportType) { if (! $useImportType->equals($fullyQualifiedObjectType) && $useImportType->areShortNamesEqual( From 19a23af675f83246e9b0417cc44e72767cde4f95 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 17 Oct 2026 15:12:56 +0000 Subject: [PATCH 2/2] [ci-review] Rector Rectify --- .../Rector/ClassMethod/ChangeMethodVisibilityRector.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php b/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php index 715e7722e0e..132ad4e8fc7 100644 --- a/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php +++ b/rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php @@ -133,12 +133,6 @@ public function configure(array $configuration): void private function hasMatchingMethodName(ClassMethod $classMethod): bool { - foreach ($this->methodVisibilities as $methodVisibility) { - if ($this->isName($classMethod, $methodVisibility->getMethod())) { - return true; - } - } - - return false; + return array_any($this->methodVisibilities, fn(ChangeMethodVisibility $methodVisibility): bool => $this->isName($classMethod, $methodVisibility->getMethod())); } }