From da1131ba00d858720958f5454bbe3181df76b34b Mon Sep 17 00:00:00 2001 From: staabm <120441+staabm@users.noreply.github.com> Date: Sat, 4 Apr 2026 08:07:33 +0000 Subject: [PATCH 01/10] List most-often-analysed trait files in -vvv diagnose output - Added ProcessedFilesCollector service to accumulate processed files during analysis - Added TraitAnalysisDiagnoseExtension to print top 5 most-analysed files in -vvv output - Modified WorkerCommand to send processedFiles in parallel worker JSON responses - Modified ParallelAnalyser to pass processedFiles through postFileCallback - Modified AnalyseApplication to collect processedFiles in both debug and non-debug modes - Added unit tests for ProcessedFilesCollector and TraitAnalysisDiagnoseExtension --- conf/services.neon | 10 +++ src/Command/AnalyseApplication.php | 10 ++- src/Command/WorkerCommand.php | 3 + src/Diagnose/ProcessedFilesCollector.php | 44 +++++++++++++ .../TraitAnalysisDiagnoseExtension.php | 39 ++++++++++++ src/Parallel/ParallelAnalyser.php | 4 +- .../Diagnose/ProcessedFilesCollectorTest.php | 62 +++++++++++++++++++ .../TraitAnalysisDiagnoseExtensionTest.php | 60 ++++++++++++++++++ 8 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 src/Diagnose/ProcessedFilesCollector.php create mode 100644 src/Diagnose/TraitAnalysisDiagnoseExtension.php create mode 100644 tests/PHPStan/Diagnose/ProcessedFilesCollectorTest.php create mode 100644 tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php diff --git a/conf/services.neon b/conf/services.neon index bccbdbcf5c4..ca2a50a6ec0 100644 --- a/conf/services.neon +++ b/conf/services.neon @@ -107,6 +107,16 @@ services: configPhpVersion: %phpVersion% autowired: false + - + class: PHPStan\Diagnose\ProcessedFilesCollector + + - + class: PHPStan\Diagnose\TraitAnalysisDiagnoseExtension + arguments: + simpleRelativePathHelper: @simpleRelativePathHelper + tags: + - phpstan.diagnoseExtension + # not registered using attributes because there is 2+ instances - class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension diff --git a/src/Command/AnalyseApplication.php b/src/Command/AnalyseApplication.php index 3b6d5842b4f..5a56de2dcce 100644 --- a/src/Command/AnalyseApplication.php +++ b/src/Command/AnalyseApplication.php @@ -10,6 +10,7 @@ use PHPStan\Analyser\ResultCache\ResultCacheManagerFactory; use PHPStan\Collectors\CollectedData; use PHPStan\DependencyInjection\AutowiredService; +use PHPStan\Diagnose\ProcessedFilesCollector; use PHPStan\Internal\BytesHelper; use PHPStan\PhpDoc\StubFilesProvider; use PHPStan\PhpDoc\StubValidator; @@ -17,6 +18,7 @@ use Symfony\Component\Console\Input\InputInterface; use function array_merge; use function array_unique; +use function array_values; use function count; use function fclose; use function feof; @@ -43,6 +45,7 @@ public function __construct( private ResultCacheManagerFactory $resultCacheManagerFactory, private IgnoredErrorHelper $ignoredErrorHelper, private StubFilesProvider $stubFilesProvider, + private ProcessedFilesCollector $processedFilesCollector, ) { } @@ -244,8 +247,9 @@ private function runAnalyser( if (!$debug) { $preFileCallback = null; - $postFileCallback = static function (int $step) use ($errorOutput): void { + $postFileCallback = function (int $step, array $processedFiles = []) use ($errorOutput): void { $errorOutput->getStyle()->progressAdvance($step); + $this->processedFilesCollector->addProcessedFiles(array_values($processedFiles)); }; $errorOutput->getStyle()->progressStart($allAnalysedFilesCount); @@ -259,7 +263,7 @@ private function runAnalyser( $postFileCallback = null; if ($stdOutput->isDebug()) { $previousMemory = memory_get_peak_usage(true); - $postFileCallback = static function (int $step, array $processedFiles = []) use ($stdOutput, &$previousMemory, &$startTime, &$linesOfCode): void { + $postFileCallback = function (int $step, array $processedFiles = []) use ($stdOutput, &$previousMemory, &$startTime, &$linesOfCode): void { if ($startTime === null) { throw new ShouldNotHappenException(); } @@ -280,6 +284,8 @@ private function runAnalyser( fclose($handle); } + $this->processedFilesCollector->addProcessedFiles(array_values($processedFiles)); + $stdOutput->writeLineFormatted(sprintf('--- consumed %s, total %s, took %.2f s, %.3f LoC/s', BytesHelper::bytes($currentTotalMemory - $previousMemory), BytesHelper::bytes($currentTotalMemory), $elapsedTime, $linesOfCode / $elapsedTime)); $previousMemory = $currentTotalMemory; }; diff --git a/src/Command/WorkerCommand.php b/src/Command/WorkerCommand.php index 2a221f6e846..d5f42b87956 100644 --- a/src/Command/WorkerCommand.php +++ b/src/Command/WorkerCommand.php @@ -228,6 +228,7 @@ private function runWorker( $dependencies = []; $usedTraitDependencies = []; $exportedNodes = []; + $processedFiles = []; foreach ($files as $file) { try { if ($file === $insteadOfFile) { @@ -242,6 +243,7 @@ private function runWorker( $dependencies[$file] = $fileAnalyserResult->getDependencies(); $usedTraitDependencies[$file] = $fileAnalyserResult->getUsedTraitDependencies(); $exportedNodes[$file] = $fileAnalyserResult->getExportedNodes(); + $processedFiles = array_merge($processedFiles, $fileAnalyserResult->getProcessedFiles()); foreach ($fileErrors as $fileError) { $errors[] = $fileError; } @@ -283,6 +285,7 @@ private function runWorker( 'usedTraitDependencies' => $usedTraitDependencies, 'exportedNodes' => $exportedNodes, 'files' => $files, + 'processedFiles' => $processedFiles, 'internalErrorsCount' => $internalErrorsCount, ]]); }); diff --git a/src/Diagnose/ProcessedFilesCollector.php b/src/Diagnose/ProcessedFilesCollector.php new file mode 100644 index 00000000000..91025ce4005 --- /dev/null +++ b/src/Diagnose/ProcessedFilesCollector.php @@ -0,0 +1,44 @@ + */ + private array $processedFiles = []; + + /** + * @param list $files + */ + public function addProcessedFiles(array $files): void + { + foreach ($files as $file) { + $this->processedFiles[] = $file; + } + } + + /** + * @return array + */ + public function getTopMostAnalysedFiles(int $limit): array + { + $counts = array_count_values($this->processedFiles); + arsort($counts); + + $result = []; + foreach (array_slice($counts, 0, $limit, true) as $file => $count) { + if ($count <= 1) { + continue; + } + $result[$file] = $count; + } + + return $result; + } + +} diff --git a/src/Diagnose/TraitAnalysisDiagnoseExtension.php b/src/Diagnose/TraitAnalysisDiagnoseExtension.php new file mode 100644 index 00000000000..0b49e87b96b --- /dev/null +++ b/src/Diagnose/TraitAnalysisDiagnoseExtension.php @@ -0,0 +1,39 @@ +processedFilesCollector->getTopMostAnalysedFiles(5); + if (count($topFiles) === 0) { + return; + } + + $output->writeLineFormatted('Most often analysed files (likely trait files):'); + foreach ($topFiles as $file => $count) { + $output->writeLineFormatted(sprintf( + ' %s: %d %s', + $this->simpleRelativePathHelper->getRelativePath($file), + $count, + $count === 1 ? 'time' : 'times', + )); + } + $output->writeLineFormatted(''); + } + +} diff --git a/src/Parallel/ParallelAnalyser.php b/src/Parallel/ParallelAnalyser.php index 7792a0cb6ba..26283e45fdd 100644 --- a/src/Parallel/ParallelAnalyser.php +++ b/src/Parallel/ParallelAnalyser.php @@ -58,7 +58,7 @@ public function __construct( } /** - * @param Closure(int ): void|null $postFileCallback + * @param Closure(int, list): void|null $postFileCallback * @param (callable(list, list, string[]): void)|null $onFileAnalysisHandler * @return PromiseInterface */ @@ -282,7 +282,7 @@ public function analyse( } if ($postFileCallback !== null) { - $postFileCallback(count($json['files'])); + $postFileCallback(count($json['files']), $json['processedFiles'] ?? []); } if (!isset($peakMemoryUsages[$processIdentifier]) || $peakMemoryUsages[$processIdentifier] < $json['memoryUsage']) { diff --git a/tests/PHPStan/Diagnose/ProcessedFilesCollectorTest.php b/tests/PHPStan/Diagnose/ProcessedFilesCollectorTest.php new file mode 100644 index 00000000000..85b3686f21d --- /dev/null +++ b/tests/PHPStan/Diagnose/ProcessedFilesCollectorTest.php @@ -0,0 +1,62 @@ +assertSame([], $collector->getTopMostAnalysedFiles(5)); + } + + public function testSingleFileNotReported(): void + { + $collector = new ProcessedFilesCollector(); + $collector->addProcessedFiles(['/path/to/file.php']); + $this->assertSame([], $collector->getTopMostAnalysedFiles(5)); + } + + public function testTopMostAnalysedFiles(): void + { + $collector = new ProcessedFilesCollector(); + + // Simulate: file A uses trait T1 and T2, file B uses trait T1 + $collector->addProcessedFiles(['/path/to/A.php', '/path/to/T1.php', '/path/to/T2.php']); + $collector->addProcessedFiles(['/path/to/B.php', '/path/to/T1.php']); + + $top = $collector->getTopMostAnalysedFiles(5); + $this->assertSame(['/path/to/T1.php' => 2], $top); + } + + public function testLimit(): void + { + $collector = new ProcessedFilesCollector(); + + // Create 7 trait files with varying usage counts + for ($i = 0; $i < 7; $i++) { + $files = ['/path/to/main' . $i . '.php']; + for ($j = 0; $j <= $i; $j++) { + $files[] = '/path/to/trait' . $j . '.php'; + } + $collector->addProcessedFiles($files); + } + + $top = $collector->getTopMostAnalysedFiles(3); + $this->assertCount(3, $top); + + // trait0.php used 7 times, trait1.php 6 times, trait2.php 5 times + $files = array_keys($top); + $this->assertSame('/path/to/trait0.php', $files[0]); + $this->assertSame(7, $top['/path/to/trait0.php']); + $this->assertSame('/path/to/trait1.php', $files[1]); + $this->assertSame(6, $top['/path/to/trait1.php']); + $this->assertSame('/path/to/trait2.php', $files[2]); + $this->assertSame(5, $top['/path/to/trait2.php']); + } + +} diff --git a/tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php b/tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php new file mode 100644 index 00000000000..5870b201cbb --- /dev/null +++ b/tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php @@ -0,0 +1,60 @@ +createOutput($lines); + + $extension->print($output); + $this->assertSame([], $lines); + } + + public function testPrintsTopFiles(): void + { + $collector = new ProcessedFilesCollector(); + $collector->addProcessedFiles(['/src/A.php', '/src/Trait1.php', '/src/Trait2.php']); + $collector->addProcessedFiles(['/src/B.php', '/src/Trait1.php', '/src/Trait2.php']); + $collector->addProcessedFiles(['/src/C.php', '/src/Trait1.php']); + + $extension = new TraitAnalysisDiagnoseExtension($collector, new NullRelativePathHelper()); + + $lines = []; + $output = $this->createOutput($lines); + + $extension->print($output); + + $this->assertCount(4, $lines); + $this->assertStringContainsString('Most often analysed files', $lines[0]); + $this->assertStringContainsString('/src/Trait1.php', $lines[1]); + $this->assertStringContainsString('3 times', $lines[1]); + $this->assertStringContainsString('/src/Trait2.php', $lines[2]); + $this->assertStringContainsString('2 times', $lines[2]); + $this->assertSame('', $lines[3]); + } + + /** + * @param list $lines + */ + private function createOutput(array &$lines): Output + { + $output = $this->createMock(Output::class); + $output->method('writeLineFormatted')->willReturnCallback(static function (string $message) use (&$lines): void { + $lines[] = $message; + }); + + return $output; + } + +} From 2cf57c3073f1a9616111e20564be30f4f9d12a24 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 4 Apr 2026 10:32:33 +0200 Subject: [PATCH 02/10] simplify --- src/Diagnose/ProcessedFilesCollector.php | 2 +- src/Diagnose/TraitAnalysisDiagnoseExtension.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Diagnose/ProcessedFilesCollector.php b/src/Diagnose/ProcessedFilesCollector.php index 91025ce4005..228420ceb25 100644 --- a/src/Diagnose/ProcessedFilesCollector.php +++ b/src/Diagnose/ProcessedFilesCollector.php @@ -23,7 +23,7 @@ public function addProcessedFiles(array $files): void } /** - * @return array + * @return array> */ public function getTopMostAnalysedFiles(int $limit): array { diff --git a/src/Diagnose/TraitAnalysisDiagnoseExtension.php b/src/Diagnose/TraitAnalysisDiagnoseExtension.php index 0b49e87b96b..bd752234b92 100644 --- a/src/Diagnose/TraitAnalysisDiagnoseExtension.php +++ b/src/Diagnose/TraitAnalysisDiagnoseExtension.php @@ -27,10 +27,9 @@ public function print(Output $output): void $output->writeLineFormatted('Most often analysed files (likely trait files):'); foreach ($topFiles as $file => $count) { $output->writeLineFormatted(sprintf( - ' %s: %d %s', + ' %s: %d times', $this->simpleRelativePathHelper->getRelativePath($file), $count, - $count === 1 ? 'time' : 'times', )); } $output->writeLineFormatted(''); From 7380d957f708373c1700a4f537395e810633c8f1 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 4 Apr 2026 10:33:10 +0200 Subject: [PATCH 03/10] Update TraitAnalysisDiagnoseExtension.php --- src/Diagnose/TraitAnalysisDiagnoseExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Diagnose/TraitAnalysisDiagnoseExtension.php b/src/Diagnose/TraitAnalysisDiagnoseExtension.php index bd752234b92..b11c85e9a8a 100644 --- a/src/Diagnose/TraitAnalysisDiagnoseExtension.php +++ b/src/Diagnose/TraitAnalysisDiagnoseExtension.php @@ -24,7 +24,7 @@ public function print(Output $output): void return; } - $output->writeLineFormatted('Most often analysed files (likely trait files):'); + $output->writeLineFormatted('Most often analysed files:'); foreach ($topFiles as $file => $count) { $output->writeLineFormatted(sprintf( ' %s: %d times', From 476ac1c5f80da000cb746e5918a7df494cdc145e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 4 Apr 2026 10:35:18 +0200 Subject: [PATCH 04/10] Update ParallelAnalyser.php --- src/Parallel/ParallelAnalyser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parallel/ParallelAnalyser.php b/src/Parallel/ParallelAnalyser.php index 26283e45fdd..10c1e6491d5 100644 --- a/src/Parallel/ParallelAnalyser.php +++ b/src/Parallel/ParallelAnalyser.php @@ -282,7 +282,7 @@ public function analyse( } if ($postFileCallback !== null) { - $postFileCallback(count($json['files']), $json['processedFiles'] ?? []); + $postFileCallback(count($json['files']), $json['processedFiles']); } if (!isset($peakMemoryUsages[$processIdentifier]) || $peakMemoryUsages[$processIdentifier] < $json['memoryUsage']) { From 2fecf3948c7dd0997c22779b82731db784727362 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 4 Apr 2026 10:37:08 +0200 Subject: [PATCH 05/10] better names --- conf/services.neon | 2 +- ...sion.php => ProcessedFilesAnalysisDiagnoseExtension.php} | 2 +- ....php => ProcessedFilesAnalysisDiagnoseExtensionTest.php} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Diagnose/{TraitAnalysisDiagnoseExtension.php => ProcessedFilesAnalysisDiagnoseExtension.php} (90%) rename tests/PHPStan/Diagnose/{TraitAnalysisDiagnoseExtensionTest.php => ProcessedFilesAnalysisDiagnoseExtensionTest.php} (84%) diff --git a/conf/services.neon b/conf/services.neon index ca2a50a6ec0..405086e6167 100644 --- a/conf/services.neon +++ b/conf/services.neon @@ -111,7 +111,7 @@ services: class: PHPStan\Diagnose\ProcessedFilesCollector - - class: PHPStan\Diagnose\TraitAnalysisDiagnoseExtension + class: PHPStan\Diagnose\ProcessedFilesAnalysisDiagnoseExtension arguments: simpleRelativePathHelper: @simpleRelativePathHelper tags: diff --git a/src/Diagnose/TraitAnalysisDiagnoseExtension.php b/src/Diagnose/ProcessedFilesAnalysisDiagnoseExtension.php similarity index 90% rename from src/Diagnose/TraitAnalysisDiagnoseExtension.php rename to src/Diagnose/ProcessedFilesAnalysisDiagnoseExtension.php index b11c85e9a8a..e9ad34e6545 100644 --- a/src/Diagnose/TraitAnalysisDiagnoseExtension.php +++ b/src/Diagnose/ProcessedFilesAnalysisDiagnoseExtension.php @@ -7,7 +7,7 @@ use function count; use function sprintf; -final class TraitAnalysisDiagnoseExtension implements DiagnoseExtension +final class ProcessedFilesAnalysisDiagnoseExtension implements DiagnoseExtension { public function __construct( diff --git a/tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php b/tests/PHPStan/Diagnose/ProcessedFilesAnalysisDiagnoseExtensionTest.php similarity index 84% rename from tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php rename to tests/PHPStan/Diagnose/ProcessedFilesAnalysisDiagnoseExtensionTest.php index 5870b201cbb..96a3055056b 100644 --- a/tests/PHPStan/Diagnose/TraitAnalysisDiagnoseExtensionTest.php +++ b/tests/PHPStan/Diagnose/ProcessedFilesAnalysisDiagnoseExtensionTest.php @@ -6,13 +6,13 @@ use PHPStan\File\NullRelativePathHelper; use PHPUnit\Framework\TestCase; -class TraitAnalysisDiagnoseExtensionTest extends TestCase +class ProcessedFilesAnalysisDiagnoseExtensionTest extends TestCase { public function testNoOutput(): void { $collector = new ProcessedFilesCollector(); - $extension = new TraitAnalysisDiagnoseExtension($collector, new NullRelativePathHelper()); + $extension = new ProcessedFilesAnalysisDiagnoseExtension($collector, new NullRelativePathHelper()); $lines = []; $output = $this->createOutput($lines); @@ -28,7 +28,7 @@ public function testPrintsTopFiles(): void $collector->addProcessedFiles(['/src/B.php', '/src/Trait1.php', '/src/Trait2.php']); $collector->addProcessedFiles(['/src/C.php', '/src/Trait1.php']); - $extension = new TraitAnalysisDiagnoseExtension($collector, new NullRelativePathHelper()); + $extension = new ProcessedFilesAnalysisDiagnoseExtension($collector, new NullRelativePathHelper()); $lines = []; $output = $this->createOutput($lines); From c587ec1595b60cba14eded3801da31681cf69fdc Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 4 Apr 2026 18:05:27 +0200 Subject: [PATCH 06/10] move files into Analyser namespace --- conf/services.neon | 4 ++-- src/{Diagnose => Analyser}/ProcessedFilesCollector.php | 2 +- .../ProcessedFilesDiagnoseExtension.php} | 5 +++-- src/Command/AnalyseApplication.php | 2 +- .../ProcessedFilesCollectorTest.php | 2 +- .../ProcessedFilesDiagnoseExtensionTest.php} | 8 ++++---- 6 files changed, 12 insertions(+), 11 deletions(-) rename src/{Diagnose => Analyser}/ProcessedFilesCollector.php (96%) rename src/{Diagnose/ProcessedFilesAnalysisDiagnoseExtension.php => Analyser/ProcessedFilesDiagnoseExtension.php} (84%) rename tests/PHPStan/{Diagnose => Analyser}/ProcessedFilesCollectorTest.php (98%) rename tests/PHPStan/{Diagnose/ProcessedFilesAnalysisDiagnoseExtensionTest.php => Analyser/ProcessedFilesDiagnoseExtensionTest.php} (83%) diff --git a/conf/services.neon b/conf/services.neon index 405086e6167..a582327381d 100644 --- a/conf/services.neon +++ b/conf/services.neon @@ -108,10 +108,10 @@ services: autowired: false - - class: PHPStan\Diagnose\ProcessedFilesCollector + class: PHPStan\Analyser\ProcessedFilesCollector - - class: PHPStan\Diagnose\ProcessedFilesAnalysisDiagnoseExtension + class: PHPStan\Analyser\ProcessedFilesDiagnoseExtension arguments: simpleRelativePathHelper: @simpleRelativePathHelper tags: diff --git a/src/Diagnose/ProcessedFilesCollector.php b/src/Analyser/ProcessedFilesCollector.php similarity index 96% rename from src/Diagnose/ProcessedFilesCollector.php rename to src/Analyser/ProcessedFilesCollector.php index 228420ceb25..efec7306268 100644 --- a/src/Diagnose/ProcessedFilesCollector.php +++ b/src/Analyser/ProcessedFilesCollector.php @@ -1,6 +1,6 @@ createOutput($lines); @@ -28,7 +28,7 @@ public function testPrintsTopFiles(): void $collector->addProcessedFiles(['/src/B.php', '/src/Trait1.php', '/src/Trait2.php']); $collector->addProcessedFiles(['/src/C.php', '/src/Trait1.php']); - $extension = new ProcessedFilesAnalysisDiagnoseExtension($collector, new NullRelativePathHelper()); + $extension = new ProcessedFilesDiagnoseExtension($collector, new NullRelativePathHelper()); $lines = []; $output = $this->createOutput($lines); From 56bb75afd0d046ed01ae90d9db9da09b09fa60ea Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 4 Apr 2026 18:11:59 +0200 Subject: [PATCH 07/10] register with attributes --- conf/services.neon | 10 ---------- src/Analyser/ProcessedFilesCollector.php | 2 ++ src/Analyser/ProcessedFilesDiagnoseExtension.php | 4 ++++ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/conf/services.neon b/conf/services.neon index a582327381d..bccbdbcf5c4 100644 --- a/conf/services.neon +++ b/conf/services.neon @@ -107,16 +107,6 @@ services: configPhpVersion: %phpVersion% autowired: false - - - class: PHPStan\Analyser\ProcessedFilesCollector - - - - class: PHPStan\Analyser\ProcessedFilesDiagnoseExtension - arguments: - simpleRelativePathHelper: @simpleRelativePathHelper - tags: - - phpstan.diagnoseExtension - # not registered using attributes because there is 2+ instances - class: PHPStan\Type\Php\ReflectionGetAttributesMethodReturnTypeExtension diff --git a/src/Analyser/ProcessedFilesCollector.php b/src/Analyser/ProcessedFilesCollector.php index efec7306268..7837ae5d874 100644 --- a/src/Analyser/ProcessedFilesCollector.php +++ b/src/Analyser/ProcessedFilesCollector.php @@ -2,10 +2,12 @@ namespace PHPStan\Analyser; +use PHPStan\DependencyInjection\AutowiredService; use function array_count_values; use function array_slice; use function arsort; +#[AutowiredService] final class ProcessedFilesCollector { diff --git a/src/Analyser/ProcessedFilesDiagnoseExtension.php b/src/Analyser/ProcessedFilesDiagnoseExtension.php index 31aceea007b..c2c273fbcca 100644 --- a/src/Analyser/ProcessedFilesDiagnoseExtension.php +++ b/src/Analyser/ProcessedFilesDiagnoseExtension.php @@ -3,16 +3,20 @@ namespace PHPStan\Analyser; use PHPStan\Command\Output; +use PHPStan\DependencyInjection\AutowiredParameter; +use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Diagnose\DiagnoseExtension; use PHPStan\File\RelativePathHelper; use function count; use function sprintf; +#[AutowiredService] final class ProcessedFilesDiagnoseExtension implements DiagnoseExtension { public function __construct( private ProcessedFilesCollector $processedFilesCollector, + #[AutowiredParameter(ref: '@simpleRelativePathHelper')] private RelativePathHelper $simpleRelativePathHelper, ) { From 38aef231cd658a076bd3d19052ce0a3c5869a540 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Sun, 5 Apr 2026 08:25:47 +0000 Subject: [PATCH 08/10] Merge ProcessedFilesDiagnoseExtension into PHPStanDiagnoseExtension Address review feedback: instead of having a separate DiagnoseExtension for processed files, merge the logic into the existing PHPStanDiagnoseExtension. Co-Authored-By: Claude Opus 4.6 --- conf/services.neon | 1 + .../ProcessedFilesDiagnoseExtension.php | 43 ------------- src/Diagnose/PHPStanDiagnoseExtension.php | 17 ++++++ .../ProcessedFilesDiagnoseExtensionTest.php | 60 ------------------- 4 files changed, 18 insertions(+), 103 deletions(-) delete mode 100644 src/Analyser/ProcessedFilesDiagnoseExtension.php delete mode 100644 tests/PHPStan/Analyser/ProcessedFilesDiagnoseExtensionTest.php diff --git a/conf/services.neon b/conf/services.neon index bccbdbcf5c4..c5c1d4019ca 100644 --- a/conf/services.neon +++ b/conf/services.neon @@ -105,6 +105,7 @@ services: composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths% allConfigFiles: %allConfigFiles% configPhpVersion: %phpVersion% + simpleRelativePathHelper: @simpleRelativePathHelper autowired: false # not registered using attributes because there is 2+ instances diff --git a/src/Analyser/ProcessedFilesDiagnoseExtension.php b/src/Analyser/ProcessedFilesDiagnoseExtension.php deleted file mode 100644 index c2c273fbcca..00000000000 --- a/src/Analyser/ProcessedFilesDiagnoseExtension.php +++ /dev/null @@ -1,43 +0,0 @@ -processedFilesCollector->getTopMostAnalysedFiles(5); - if (count($topFiles) === 0) { - return; - } - - $output->writeLineFormatted('Most often analysed files:'); - foreach ($topFiles as $file => $count) { - $output->writeLineFormatted(sprintf( - ' %s: %d times', - $this->simpleRelativePathHelper->getRelativePath($file), - $count, - )); - } - $output->writeLineFormatted(''); - } - -} diff --git a/src/Diagnose/PHPStanDiagnoseExtension.php b/src/Diagnose/PHPStanDiagnoseExtension.php index 35cb6a862e4..17bda3c754f 100644 --- a/src/Diagnose/PHPStanDiagnoseExtension.php +++ b/src/Diagnose/PHPStanDiagnoseExtension.php @@ -3,9 +3,11 @@ namespace PHPStan\Diagnose; use Phar; +use PHPStan\Analyser\ProcessedFilesCollector; use PHPStan\Command\Output; use PHPStan\ExtensionInstaller\GeneratedConfig; use PHPStan\File\FileHelper; +use PHPStan\File\RelativePathHelper; use PHPStan\Internal\ComposerHelper; use PHPStan\Php\ComposerPhpVersionFactory; use PHPStan\Php\PhpVersion; @@ -42,6 +44,8 @@ public function __construct( private array $composerAutoloaderProjectPaths, private array $allConfigFiles, private ComposerPhpVersionFactory $composerPhpVersionFactory, + private ProcessedFilesCollector $processedFilesCollector, + private RelativePathHelper $simpleRelativePathHelper, ) { } @@ -203,6 +207,19 @@ public function print(Output $output): void $output->writeLineFormatted($composerAutoloaderProjectPath); } $output->writeLineFormatted(''); + + $topFiles = $this->processedFilesCollector->getTopMostAnalysedFiles(5); + if (count($topFiles) > 0) { + $output->writeLineFormatted('Most often analysed files:'); + foreach ($topFiles as $file => $count) { + $output->writeLineFormatted(sprintf( + ' %s: %d times', + $this->simpleRelativePathHelper->getRelativePath($file), + $count, + )); + } + $output->writeLineFormatted(''); + } } } diff --git a/tests/PHPStan/Analyser/ProcessedFilesDiagnoseExtensionTest.php b/tests/PHPStan/Analyser/ProcessedFilesDiagnoseExtensionTest.php deleted file mode 100644 index 42ee7decd36..00000000000 --- a/tests/PHPStan/Analyser/ProcessedFilesDiagnoseExtensionTest.php +++ /dev/null @@ -1,60 +0,0 @@ -createOutput($lines); - - $extension->print($output); - $this->assertSame([], $lines); - } - - public function testPrintsTopFiles(): void - { - $collector = new ProcessedFilesCollector(); - $collector->addProcessedFiles(['/src/A.php', '/src/Trait1.php', '/src/Trait2.php']); - $collector->addProcessedFiles(['/src/B.php', '/src/Trait1.php', '/src/Trait2.php']); - $collector->addProcessedFiles(['/src/C.php', '/src/Trait1.php']); - - $extension = new ProcessedFilesDiagnoseExtension($collector, new NullRelativePathHelper()); - - $lines = []; - $output = $this->createOutput($lines); - - $extension->print($output); - - $this->assertCount(4, $lines); - $this->assertStringContainsString('Most often analysed files', $lines[0]); - $this->assertStringContainsString('/src/Trait1.php', $lines[1]); - $this->assertStringContainsString('3 times', $lines[1]); - $this->assertStringContainsString('/src/Trait2.php', $lines[2]); - $this->assertStringContainsString('2 times', $lines[2]); - $this->assertSame('', $lines[3]); - } - - /** - * @param list $lines - */ - private function createOutput(array &$lines): Output - { - $output = $this->createMock(Output::class); - $output->method('writeLineFormatted')->willReturnCallback(static function (string $message) use (&$lines): void { - $lines[] = $message; - }); - - return $output; - } - -} From 09d35660ee2bfed517342009dfad14337f66866a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 5 Apr 2026 19:38:52 +0200 Subject: [PATCH 09/10] fix --- src/Command/AnalyseApplication.php | 4 ++-- src/Diagnose/PHPStanDiagnoseExtension.php | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Command/AnalyseApplication.php b/src/Command/AnalyseApplication.php index c1635e53637..73ba73ca37f 100644 --- a/src/Command/AnalyseApplication.php +++ b/src/Command/AnalyseApplication.php @@ -247,7 +247,7 @@ private function runAnalyser( if (!$debug) { $preFileCallback = null; - $postFileCallback = function (int $step, array $processedFiles = []) use ($errorOutput): void { + $postFileCallback = function (int $step, array $processedFiles) use ($errorOutput): void { $errorOutput->getStyle()->progressAdvance($step); $this->processedFilesCollector->addProcessedFiles(array_values($processedFiles)); }; @@ -263,7 +263,7 @@ private function runAnalyser( $postFileCallback = null; if ($stdOutput->isDebug()) { $previousMemory = memory_get_peak_usage(true); - $postFileCallback = function (int $step, array $processedFiles = []) use ($stdOutput, &$previousMemory, &$startTime, &$linesOfCode): void { + $postFileCallback = function (int $step, array $processedFiles) use ($stdOutput, &$previousMemory, &$startTime, &$linesOfCode): void { if ($startTime === null) { throw new ShouldNotHappenException(); } diff --git a/src/Diagnose/PHPStanDiagnoseExtension.php b/src/Diagnose/PHPStanDiagnoseExtension.php index 17bda3c754f..d35951c578c 100644 --- a/src/Diagnose/PHPStanDiagnoseExtension.php +++ b/src/Diagnose/PHPStanDiagnoseExtension.php @@ -209,17 +209,19 @@ public function print(Output $output): void $output->writeLineFormatted(''); $topFiles = $this->processedFilesCollector->getTopMostAnalysedFiles(5); - if (count($topFiles) > 0) { - $output->writeLineFormatted('Most often analysed files:'); - foreach ($topFiles as $file => $count) { - $output->writeLineFormatted(sprintf( - ' %s: %d times', - $this->simpleRelativePathHelper->getRelativePath($file), - $count, - )); - } - $output->writeLineFormatted(''); + if (count($topFiles) <= 0) { + return; } + + $output->writeLineFormatted('Most often analysed files:'); + foreach ($topFiles as $file => $count) { + $output->writeLineFormatted(sprintf( + ' %s: %d times', + $this->simpleRelativePathHelper->getRelativePath($file), + $count, + )); + } + $output->writeLineFormatted(''); } } From e2b93b3851715916e3bcad2cfaeecf29b1852848 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 5 Apr 2026 19:42:14 +0200 Subject: [PATCH 10/10] Update AnalyserRunner.php --- src/Command/AnalyserRunner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/AnalyserRunner.php b/src/Command/AnalyserRunner.php index 1da03e53ed3..3447d6457a8 100644 --- a/src/Command/AnalyserRunner.php +++ b/src/Command/AnalyserRunner.php @@ -37,7 +37,7 @@ public function __construct( * @param string[] $files * @param string[] $allAnalysedFiles * @param Closure(string $file): void|null $preFileCallback - * @param Closure(int, list=): void|null $postFileCallback + * @param Closure(int, list): void|null $postFileCallback */ public function runAnalyser( array $files,