From f78b818ffbd0397258745a6638c6a15f90c5269b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 4 Sep 2026 23:24:03 +0700 Subject: [PATCH] refactor: simplify worker payload merge in ParallelAnalysisNodeExtractor with array_push() --- .../ParallelAnalysisNodeExtractor.php | 137 +-------- .../ParallelAnalysisNodeExtractorTest.php | 268 ------------------ 2 files changed, 15 insertions(+), 390 deletions(-) diff --git a/src/Analyser/Parallel/ParallelAnalysisNodeExtractor.php b/src/Analyser/Parallel/ParallelAnalysisNodeExtractor.php index 3e29e473..a6c4e457 100644 --- a/src/Analyser/Parallel/ParallelAnalysisNodeExtractor.php +++ b/src/Analyser/Parallel/ParallelAnalysisNodeExtractor.php @@ -18,6 +18,7 @@ use function array_fill; use function array_key_exists; use function array_keys; +use function array_push; use function array_search; use function arsort; use function assert; @@ -243,134 +244,26 @@ public function extract( /** @var list $workerNodes */ $workerNodes = $result['nodes']; - - foreach ($workerNodes as $workerNode) { - $nodes[] = $workerNode; - } - + /** @var array $workerFileAnalyses */ $workerFileAnalyses = $result['fileAnalyses'] ?? []; - - if (! is_array($workerFileAnalyses)) { - throw new RuntimeException('Parallel analysis worker returned invalid file analyses.'); - } - - foreach ($workerFileAnalyses as $file => $fileAnalysis) { - if (! is_string($file) || ! $fileAnalysis instanceof FileAnalysis) { - throw new RuntimeException('Parallel analysis worker returned invalid file analyses.'); - } - - $fileAnalyses[$file] = $fileAnalysis; - } - - $workerAnonClassNodes = $result['anonymousClassNodes'] ?? []; - - if (! is_array($workerAnonClassNodes)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid anonymous class nodes.' - ); - } - - foreach ($workerAnonClassNodes as $workerAnonClassNode) { - if (! $workerAnonClassNode instanceof AnonymousClassNode) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid anonymous class nodes.' - ); - } - - $anonymousClassNodes[] = $workerAnonClassNode; - } - + /** @var list $workerAnonymousClassNodes */ + $workerAnonymousClassNodes = $result['anonymousClassNodes'] ?? []; + /** @var array> $workerFileReferences */ $workerFileReferences = $result['fileReferences'] ?? []; - - if (! is_array($workerFileReferences)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid file references.' - ); - } - - foreach ($workerFileReferences as $file => $references) { - if (! is_string($file) || ! is_array($references)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid file references.' - ); - } - - $validReferences = []; - - foreach ($references as $reference) { - if (! is_string($reference)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid file references.' - ); - } - - $validReferences[] = $reference; - } - - $fileReferences[$file] = $validReferences; - } - + /** @var array> $workerFileInstantiations */ $workerFileInstantiations = $result['fileInstantiations'] ?? []; - - if (! is_array($workerFileInstantiations)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid file instantiations.' - ); - } - - foreach ($workerFileInstantiations as $file => $instantiations) { - if (! is_string($file) || ! is_array($instantiations)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid file instantiations.' - ); - } - - $validInstantiations = []; - - foreach ($instantiations as $instantiation) { - if (! is_string($instantiation)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid file instantiations.' - ); - } - - $validInstantiations[] = $instantiation; - } - - $fileInstantiations[$file] = $validInstantiations; - } - + /** @var list $workerFunctionNodes */ $workerFunctionNodes = $result['functionNodes'] ?? []; - - if (! is_array($workerFunctionNodes)) { - throw new RuntimeException('Parallel analysis worker returned invalid function nodes.'); - } - - foreach ($workerFunctionNodes as $workerFunctionNode) { - if (! $workerFunctionNode instanceof FunctionNode) { - throw new RuntimeException('Parallel analysis worker returned invalid function nodes.'); - } - - $functionNodes[] = $workerFunctionNode; - } - + /** @var list $workerAnonymousFunctionNodes */ $workerAnonymousFunctionNodes = $result['anonymousFunctionNodes'] ?? []; - if (! is_array($workerAnonymousFunctionNodes)) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid anonymous function nodes.' - ); - } - - foreach ($workerAnonymousFunctionNodes as $workerAnonymousFunctionNode) { - if (! $workerAnonymousFunctionNode instanceof AnonymousFunctionNode) { - throw new RuntimeException( - 'Parallel analysis worker returned invalid anonymous function nodes.' - ); - } - - $anonymousFunctionNodes[] = $workerAnonymousFunctionNode; - } + array_push($nodes, ...$workerNodes); + array_push($anonymousClassNodes, ...$workerAnonymousClassNodes); + array_push($functionNodes, ...$workerFunctionNodes); + array_push($anonymousFunctionNodes, ...$workerAnonymousFunctionNodes); + $fileAnalyses += $workerFileAnalyses; + $fileReferences += $workerFileReferences; + $fileInstantiations += $workerFileInstantiations; } catch (RuntimeException $runtimeException) { $failure ??= $runtimeException->getMessage(); } finally { diff --git a/tests/Analyser/Parallel/ParallelAnalysisNodeExtractorTest.php b/tests/Analyser/Parallel/ParallelAnalysisNodeExtractorTest.php index d1e298b6..0e81c299 100644 --- a/tests/Analyser/Parallel/ParallelAnalysisNodeExtractorTest.php +++ b/tests/Analyser/Parallel/ParallelAnalysisNodeExtractorTest.php @@ -9,9 +9,7 @@ use Boundwize\StructArmed\Cache\AnalysisResultCache; use Boundwize\StructArmed\Cache\FileHashProvider; use Boundwize\StructArmed\Tests\Support\TemporaryDirectoryCleanupTrait; -use Iterator; use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -446,270 +444,4 @@ public function testExtractThrowsWhenErrorPayloadIsInvalid(): void $GLOBALS['mock_tracked_tempnam_files'] = []; } } - - public function testExtractThrowsWhenFileAnalysesPayloadIsNotAnArray(): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => 'invalid', - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid file analyses.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - public function testExtractThrowsWhenFileAnalysisEntryIsInvalid(): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => ['Foo.php' => 'invalid'], - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid file analyses.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - public function testExtractThrowsWhenAnonymousClassNodesPayloadIsNotAnArray(): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'anonymousClassNodes' => 'invalid', - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid anonymous class nodes.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - public function testExtractThrowsWhenAnonymousClassNodeEntryIsInvalid(): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'anonymousClassNodes' => ['invalid'], - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid anonymous class nodes.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - public function testExtractThrowsWhenFileReferencesPayloadIsNotAnArray(): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'anonymousClassNodes' => [], - 'fileReferences' => 'invalid', - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid file references.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - /** - * @return Iterator - */ - public static function invalidFileReferencesEntryProvider(): Iterator - { - yield 'entry not an array' => [['Foo.php' => 'invalid']]; - yield 'entry with non-string reference' => [['Foo.php' => [1]]]; - } - - /** - * @return Iterator - */ - public static function invalidFileInstantiationsProvider(): Iterator - { - yield 'not an array' => ['invalid']; - yield 'entry not an array' => [['Foo.php' => 'invalid']]; - yield 'entry with non-string instantiation' => [['Foo.php' => [1]]]; - } - - #[DataProvider('invalidFileInstantiationsProvider')] - public function testExtractThrowsWhenFileInstantiationsPayloadIsInvalid(mixed $invalidFileInstantiations): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'anonymousClassNodes' => [], - 'fileReferences' => [], - 'fileInstantiations' => $invalidFileInstantiations, - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid file instantiations.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - #[DataProvider('invalidFunctionNodesProvider')] - public function testExtractThrowsWhenFunctionNodesPayloadIsInvalid(mixed $invalidFunctionNodes): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'functionNodes' => $invalidFunctionNodes, - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid function nodes.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - /** @return Iterator */ - public static function invalidFunctionNodesProvider(): Iterator - { - yield 'not an array' => ['invalid']; - yield 'entry is not a node' => [['invalid']]; - } - - #[DataProvider('invalidFunctionNodesProvider')] - public function testExtractThrowsWhenAnonymousFunctionNodesPayloadIsInvalid(mixed $invalidNodes): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'anonymousFunctionNodes' => $invalidNodes, - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid anonymous function nodes.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } - - #[DataProvider('invalidFileReferencesEntryProvider')] - public function testExtractThrowsWhenFileReferencesEntryIsInvalid(mixed $invalidFileReferences): void - { - $GLOBALS['mock_file_get_contents_payload'] = [ - 'nodes' => [], - 'fileAnalyses' => [], - 'anonymousClassNodes' => [], - 'fileReferences' => $invalidFileReferences, - 'error' => null, - ]; - - $dir = $this->makeTemporaryDirectory('structarmed-parallel-test'); - $file = $dir . '/Foo.php'; - file_put_contents($file, 'expectException(RuntimeException::class); - $this->expectExceptionMessage('Parallel analysis worker returned invalid file references.'); - - try { - $parallelAnalysisNodeExtractor->extract([$file]); - } finally { - $GLOBALS['mock_file_get_contents_payload'] = null; - $GLOBALS['mock_tracked_tempnam_files'] = []; - } - } }