From 7a5c94a5efcd45abc03f17c3328545e553ede02e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 11 Jul 2026 18:04:02 +0200 Subject: [PATCH 1/3] Faster DuplicateFunctionDeclarationRule --- .../DuplicateFunctionDeclarationRule.php | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Rules/Functions/DuplicateFunctionDeclarationRule.php b/src/Rules/Functions/DuplicateFunctionDeclarationRule.php index f9dfa97e906..24fa4eee689 100644 --- a/src/Rules/Functions/DuplicateFunctionDeclarationRule.php +++ b/src/Rules/Functions/DuplicateFunctionDeclarationRule.php @@ -23,6 +23,9 @@ final class DuplicateFunctionDeclarationRule implements Rule { + /** @var array>|null */ + private ?array $functionMap = null; + public function __construct(private Reflector $reflector, private RelativePathHelper $relativePathHelper) { } @@ -35,25 +38,31 @@ public function getNodeType(): string public function processNode(Node $node, Scope $scope): array { $thisFunction = $node->getFunctionReflection(); - $allFunctions = $this->reflector->reflectAllFunctions(); - $filteredFunctions = []; - foreach ($allFunctions as $reflectionFunction) { - if ($reflectionFunction->getName() !== $thisFunction->getName()) { - continue; - } + $functionName = $thisFunction->getName(); - $filteredFunctions[] = $reflectionFunction; + if ($this->functionMap === null) { + $this->functionMap = []; + + $allFunctions = $this->reflector->reflectAllFunctions(); + $filteredFunctions = []; + foreach ($allFunctions as $reflectionFunction) { + $reflectionFunctionName = $reflectionFunction->getName(); + if (!isset($this->functionMap[$reflectionFunctionName])) { + $this->functionMap[$reflectionFunctionName] = []; + } + $this->functionMap[$reflectionFunctionName][] = $reflectionFunction; + } } - if (count($filteredFunctions) < 2) { + if (!isset($this->functionMap[$functionName]) || count($this->functionMap[$functionName]) < 2) { return []; } return [ RuleErrorBuilder::message(sprintf( "Function %s declared multiple times:\n%s", - $thisFunction->getName(), - implode("\n", array_map(fn (ReflectionFunction $function) => sprintf('- %s:%d', $this->relativePathHelper->getRelativePath($function->getFileName() ?? 'unknown'), $function->getStartLine()), $filteredFunctions)), + $functionName, + implode("\n", array_map(fn (ReflectionFunction $function) => sprintf('- %s:%d', $this->relativePathHelper->getRelativePath($function->getFileName() ?? 'unknown'), $function->getStartLine()), $this->functionMap[$functionName])), ))->identifier('function.duplicate')->build(), ]; } From 5af2a91fbaab78997d0dcf07150746a8ea5f6ca9 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 11 Jul 2026 18:13:52 +0200 Subject: [PATCH 2/3] cs --- src/Rules/Functions/DuplicateFunctionDeclarationRule.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rules/Functions/DuplicateFunctionDeclarationRule.php b/src/Rules/Functions/DuplicateFunctionDeclarationRule.php index 24fa4eee689..4782432fe54 100644 --- a/src/Rules/Functions/DuplicateFunctionDeclarationRule.php +++ b/src/Rules/Functions/DuplicateFunctionDeclarationRule.php @@ -44,7 +44,6 @@ public function processNode(Node $node, Scope $scope): array $this->functionMap = []; $allFunctions = $this->reflector->reflectAllFunctions(); - $filteredFunctions = []; foreach ($allFunctions as $reflectionFunction) { $reflectionFunctionName = $reflectionFunction->getName(); if (!isset($this->functionMap[$reflectionFunctionName])) { From 0fec0195cc62574c71e8c369954ca128299b6cff Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 11 Jul 2026 22:18:22 +0200 Subject: [PATCH 3/3] docs --- src/Rules/Functions/DuplicateFunctionDeclarationRule.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Rules/Functions/DuplicateFunctionDeclarationRule.php b/src/Rules/Functions/DuplicateFunctionDeclarationRule.php index 4782432fe54..25006e0b95c 100644 --- a/src/Rules/Functions/DuplicateFunctionDeclarationRule.php +++ b/src/Rules/Functions/DuplicateFunctionDeclarationRule.php @@ -40,6 +40,8 @@ public function processNode(Node $node, Scope $scope): array $thisFunction = $node->getFunctionReflection(); $functionName = $thisFunction->getName(); + // this rule runs at the very end of the analysis, + // so all function already have been discovered at this point. if ($this->functionMap === null) { $this->functionMap = [];