From 7c92a47b1a2d925559179d0f65385c9fe300ac64 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Wed, 18 Feb 2026 10:22:58 +0100 Subject: [PATCH 01/13] Ignore `loader.neon` to ensure compatibility with split baseline created with shipmonk/phpstan-baseline-per-identifier --- lib/BaselineFinder.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/BaselineFinder.php b/lib/BaselineFinder.php index 084ab61..e2c1bf1 100644 --- a/lib/BaselineFinder.php +++ b/lib/BaselineFinder.php @@ -16,6 +16,10 @@ static public function forGlob(string $glob): array continue; } + if (str_ends_with($baseline, 'loader.neon')) { + continue; + } + if (!str_ends_with($baseline, '.neon') && !str_ends_with($baseline, '.php')) { continue; } From 8bf90c65201b64464299ed1431fd5eca5afb2a47 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 10:40:52 +0100 Subject: [PATCH 02/13] Add comment --- lib/BaselineFinder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/BaselineFinder.php b/lib/BaselineFinder.php index e2c1bf1..e07f6df 100644 --- a/lib/BaselineFinder.php +++ b/lib/BaselineFinder.php @@ -16,6 +16,7 @@ static public function forGlob(string $glob): array continue; } + // Skip loader.neon, which is used for loading baselines in phpstan-baseline-filter, but is not a baseline itself. if (str_ends_with($baseline, 'loader.neon')) { continue; } From 662a4b8fadda1b1c88a3375ca5511404dca50bae Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 10:42:32 +0100 Subject: [PATCH 03/13] Add method `summarize` --- lib/AnalyzeApplication.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/AnalyzeApplication.php b/lib/AnalyzeApplication.php index 1d5ec7d..1c47c91 100644 --- a/lib/AnalyzeApplication.php +++ b/lib/AnalyzeApplication.php @@ -51,6 +51,41 @@ public function start(string $glob, string $format): int return self::EXIT_ERROR; } + public function summarize(string $glob, string $format): int + { + $baselines = BaselineFinder::forGlob($glob); + $numBaselines = count($baselines); + if ($numBaselines === 0) { + return self::EXIT_ERROR; + } + + $resultSummary = new AnalyzerResult(); + $baselineSummary = new Baseline(); + + foreach ($baselines as $baseline) { + $analyzer = new BaselineAnalyzer($baseline); + $result = $analyzer->analyze(); + $resultSummary->overallErrors += $result->overallErrors; + $resultSummary->deprecations += $result->deprecations; + $resultSummary->invalidPhpdocs += $result->invalidPhpdocs; + $resultSummary->unknownTypes += $result->unknownTypes; + $resultSummary->anonymousVariables += $result->anonymousVariables; + $resultSummary->unusedSymbols += $result->unusedSymbols; + } + + $printer = new ResultPrinter(); + + if ($format == ResultPrinter::FORMAT_JSON) { + $stream = $printer->streamJson($baselineSummary, $resultSummary); + } else { + $stream = $printer->streamText($baselineSummary, $resultSummary); + } + + $this->printSummary($format, $stream); + + return self::EXIT_SUCCESS; + } + /** * @api */ From 583428e4c0b0647e5f65ff127cef4d234ec3a015 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 10:43:11 +0100 Subject: [PATCH 04/13] Add method to output summary --- lib/AnalyzeApplication.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/AnalyzeApplication.php b/lib/AnalyzeApplication.php index 1c47c91..79c4543 100644 --- a/lib/AnalyzeApplication.php +++ b/lib/AnalyzeApplication.php @@ -119,4 +119,26 @@ private function printResult(string $format, bool $isFirst, bool $isLast, Iterat } } } + + /** + * @param Iterator $stream + */ + private function printSummary(string $format, Iterator $stream): void + { + if ($format === ResultPrinter::FORMAT_JSON) { + printf('['); + } + + foreach ($stream as $string) { + printf($string); + + if ($format === ResultPrinter::FORMAT_JSON) { + printf(",\n"); + } + } + + if ($format === ResultPrinter::FORMAT_JSON) { + printf(']'); + } + } } From a1c870f2812b43a8f8f011d2dfc3e9aa9fa24ad2 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 10:44:41 +0100 Subject: [PATCH 05/13] Return project dirname if filePath is not set inside the baseline --- lib/Baseline.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Baseline.php b/lib/Baseline.php index 1273250..89e4f5a 100644 --- a/lib/Baseline.php +++ b/lib/Baseline.php @@ -59,6 +59,6 @@ public function getIgnoreErrors(): Iterator { } public function getFilePath():string { - return $this->filePath; + return $this->filePath ?? basename(getcwd()); } } From d5175e1026ced17e594cdab438e0089b0521afb7 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 10:45:11 +0100 Subject: [PATCH 06/13] Add new option `--summary` --- bin/phpstan-baseline-analyze.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/phpstan-baseline-analyze.php b/bin/phpstan-baseline-analyze.php index 374dbea..16ab19f 100644 --- a/bin/phpstan-baseline-analyze.php +++ b/bin/phpstan-baseline-analyze.php @@ -38,5 +38,10 @@ $format = ResultPrinter::FORMAT_JSON; } -$exitCode = $app->start($argv[1], $format); +if (in_array('--summary', $argv)) { + $exitCode = $app->summarize($argv[1], $format); +} else { + $exitCode = $app->start($argv[1], $format); +} + exit($exitCode); From c7101c6128d5ce7dd2ea50460a53db2becd71374 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 11:28:52 +0100 Subject: [PATCH 07/13] Remove trailing comma --- lib/AnalyzeApplication.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/AnalyzeApplication.php b/lib/AnalyzeApplication.php index 79c4543..f297b9f 100644 --- a/lib/AnalyzeApplication.php +++ b/lib/AnalyzeApplication.php @@ -131,10 +131,6 @@ private function printSummary(string $format, Iterator $stream): void foreach ($stream as $string) { printf($string); - - if ($format === ResultPrinter::FORMAT_JSON) { - printf(",\n"); - } } if ($format === ResultPrinter::FORMAT_JSON) { From dd73b482300134e182b30403e60347bacb06cfd6 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 11:37:34 +0100 Subject: [PATCH 08/13] Set reference date --- lib/AnalyzeApplication.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/AnalyzeApplication.php b/lib/AnalyzeApplication.php index f297b9f..b11adbc 100644 --- a/lib/AnalyzeApplication.php +++ b/lib/AnalyzeApplication.php @@ -2,6 +2,7 @@ namespace staabm\PHPStanBaselineAnalysis; +use DateTimeImmutable; use \Iterator; final class AnalyzeApplication @@ -65,6 +66,7 @@ public function summarize(string $glob, string $format): int foreach ($baselines as $baseline) { $analyzer = new BaselineAnalyzer($baseline); $result = $analyzer->analyze(); + $resultSummary->referenceDate = new DateTimeImmutable(); $resultSummary->overallErrors += $result->overallErrors; $resultSummary->deprecations += $result->deprecations; $resultSummary->invalidPhpdocs += $result->invalidPhpdocs; From 27c4580d6fa8b655586ac65d3d02ec9f127489e7 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 26 Feb 2026 12:03:41 +0100 Subject: [PATCH 09/13] PHPStan --- lib/Baseline.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Baseline.php b/lib/Baseline.php index 89e4f5a..9c82a2d 100644 --- a/lib/Baseline.php +++ b/lib/Baseline.php @@ -59,6 +59,6 @@ public function getIgnoreErrors(): Iterator { } public function getFilePath():string { - return $this->filePath ?? basename(getcwd()); + return $this->filePath ? : basename(getcwd()); } } From 5240d5eab04235869ef33a30faf43ac62b134ce5 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 9 Apr 2026 10:21:46 +0200 Subject: [PATCH 10/13] Add optional `--exclude=` to CLI --- bin/phpstan-baseline-analyze.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/phpstan-baseline-analyze.php b/bin/phpstan-baseline-analyze.php index 16ab19f..134997a 100644 --- a/bin/phpstan-baseline-analyze.php +++ b/bin/phpstan-baseline-analyze.php @@ -38,8 +38,11 @@ $format = ResultPrinter::FORMAT_JSON; } +$excludeOptions = preg_grep('/^--exclude=/', $argv); +$excludedFiles = preg_replace('/^--exclude=/', '', $excludeOptions); + if (in_array('--summary', $argv)) { - $exitCode = $app->summarize($argv[1], $format); + $exitCode = $app->summarize($argv[1], $format, $excludedFiles); } else { $exitCode = $app->start($argv[1], $format); } From 8384dbcd9ac20a9aef6211d71f5030575c2faa77 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 9 Apr 2026 10:23:43 +0200 Subject: [PATCH 11/13] Pass filenames to exclude around --- lib/AnalyzeApplication.php | 7 +++++-- lib/BaselineFinder.php | 10 ++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/AnalyzeApplication.php b/lib/AnalyzeApplication.php index b11adbc..22d1efe 100644 --- a/lib/AnalyzeApplication.php +++ b/lib/AnalyzeApplication.php @@ -52,9 +52,12 @@ public function start(string $glob, string $format): int return self::EXIT_ERROR; } - public function summarize(string $glob, string $format): int + /** + * @param string[] $excludedFilenames + */ + public function summarize(string $glob, string $format, array $excludedFilenames = []): int { - $baselines = BaselineFinder::forGlob($glob); + $baselines = BaselineFinder::forGlob($glob, $excludedFilenames); $numBaselines = count($baselines); if ($numBaselines === 0) { return self::EXIT_ERROR; diff --git a/lib/BaselineFinder.php b/lib/BaselineFinder.php index e07f6df..61fc1dc 100644 --- a/lib/BaselineFinder.php +++ b/lib/BaselineFinder.php @@ -5,13 +5,14 @@ final class BaselineFinder { /** + * @param string[] $excludeFilenames * @return Baseline[] */ - static public function forGlob(string $glob): array + static public function forGlob(string $glob, array $excludeFilenames = []): array { $baselines = []; - foreach (self::rglob($glob) as $baseline) { + foreach (self::rglob($glob, 0, $excludeFilenames) as $baseline) { if (!is_file($baseline)) { continue; } @@ -34,9 +35,10 @@ static public function forGlob(string $glob): array /** * from https://stackoverflow.com/a/17161106 * + * @param string[] $excludeFilenames * @return string[] */ - static private function rglob(string $pattern,int $flags = 0):array + static private function rglob(string $pattern, int $flags = 0, array $excludeFilenames = []): array { $files = glob($pattern, $flags); if (!$files) { @@ -48,7 +50,7 @@ static private function rglob(string $pattern,int $flags = 0):array continue; } - $files = array_merge($files, self::rglob($dir . '/' . basename($pattern), $flags)); + $files = array_merge($files, self::rglob($dir . '/' . basename($pattern), $flags, $excludeFilenames)); } return $files; } From 9b7a8e5520f5ff0c92449dab84a268df13c2a000 Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 9 Apr 2026 10:24:33 +0200 Subject: [PATCH 12/13] Filter excluded filenames from array --- lib/BaselineFinder.php | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/BaselineFinder.php b/lib/BaselineFinder.php index 61fc1dc..410fb3d 100644 --- a/lib/BaselineFinder.php +++ b/lib/BaselineFinder.php @@ -17,11 +17,6 @@ static public function forGlob(string $glob, array $excludeFilenames = []): arra continue; } - // Skip loader.neon, which is used for loading baselines in phpstan-baseline-filter, but is not a baseline itself. - if (str_ends_with($baseline, 'loader.neon')) { - continue; - } - if (!str_ends_with($baseline, '.neon') && !str_ends_with($baseline, '.php')) { continue; } @@ -45,6 +40,11 @@ static private function rglob(string $pattern, int $flags = 0, array $excludeFil return []; } + // Filter out excluded filenames + if ($excludeFilenames !== []) { + $files = self::filerExcludedFiles($files, $excludeFilenames); + } + foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) ?: [] as $dir) { if (basename($dir) == 'vendor') { continue; @@ -54,4 +54,22 @@ static private function rglob(string $pattern, int $flags = 0, array $excludeFil } return $files; } + + /** + * @param string[] $files + * @param string[] $excludeFilenames + * @return array + */ + private static function filerExcludedFiles(array $files, array $excludeFilenames): array + { + return array_filter($files, function (string $file) use ($excludeFilenames) { + foreach ($excludeFilenames as $excludeFilename) { + if (str_ends_with($file, $excludeFilename)) { + return false; + } + } + + return true; + }); + } } From c36f4f94153b43b67f4d89e4ff39aa651e1ccb7a Mon Sep 17 00:00:00 2001 From: clxswalther Date: Thu, 9 Apr 2026 10:57:50 +0200 Subject: [PATCH 13/13] Make PHPStan a little happier --- bin/phpstan-baseline-analyze.php | 6 +++++- lib/BaselineFinder.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/phpstan-baseline-analyze.php b/bin/phpstan-baseline-analyze.php index 134997a..4c7bc72 100644 --- a/bin/phpstan-baseline-analyze.php +++ b/bin/phpstan-baseline-analyze.php @@ -39,7 +39,11 @@ } $excludeOptions = preg_grep('/^--exclude=/', $argv); -$excludedFiles = preg_replace('/^--exclude=/', '', $excludeOptions); +if ($excludeOptions) { + $excludedFiles = preg_replace('/^--exclude=/', '', $excludeOptions); +} else { + $excludedFiles = []; +} if (in_array('--summary', $argv)) { $exitCode = $app->summarize($argv[1], $format, $excludedFiles); diff --git a/lib/BaselineFinder.php b/lib/BaselineFinder.php index 410fb3d..4daa964 100644 --- a/lib/BaselineFinder.php +++ b/lib/BaselineFinder.php @@ -58,7 +58,7 @@ static private function rglob(string $pattern, int $flags = 0, array $excludeFil /** * @param string[] $files * @param string[] $excludeFilenames - * @return array + * @return string[] */ private static function filerExcludedFiles(array $files, array $excludeFilenames): array {