diff --git a/bin/phpstan-baseline-analyze.php b/bin/phpstan-baseline-analyze.php index 374dbea..4c7bc72 100644 --- a/bin/phpstan-baseline-analyze.php +++ b/bin/phpstan-baseline-analyze.php @@ -38,5 +38,17 @@ $format = ResultPrinter::FORMAT_JSON; } -$exitCode = $app->start($argv[1], $format); +$excludeOptions = preg_grep('/^--exclude=/', $argv); +if ($excludeOptions) { + $excludedFiles = preg_replace('/^--exclude=/', '', $excludeOptions); +} else { + $excludedFiles = []; +} + +if (in_array('--summary', $argv)) { + $exitCode = $app->summarize($argv[1], $format, $excludedFiles); +} else { + $exitCode = $app->start($argv[1], $format); +} + exit($exitCode); diff --git a/lib/AnalyzeApplication.php b/lib/AnalyzeApplication.php index 1d5ec7d..22d1efe 100644 --- a/lib/AnalyzeApplication.php +++ b/lib/AnalyzeApplication.php @@ -2,6 +2,7 @@ namespace staabm\PHPStanBaselineAnalysis; +use DateTimeImmutable; use \Iterator; final class AnalyzeApplication @@ -51,6 +52,45 @@ public function start(string $glob, string $format): int return self::EXIT_ERROR; } + /** + * @param string[] $excludedFilenames + */ + public function summarize(string $glob, string $format, array $excludedFilenames = []): int + { + $baselines = BaselineFinder::forGlob($glob, $excludedFilenames); + $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->referenceDate = new DateTimeImmutable(); + $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 */ @@ -84,4 +124,22 @@ 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(']'); + } + } } diff --git a/lib/Baseline.php b/lib/Baseline.php index 1273250..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; + return $this->filePath ? : basename(getcwd()); } } diff --git a/lib/BaselineFinder.php b/lib/BaselineFinder.php index 084ab61..4daa964 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; } @@ -29,22 +30,46 @@ 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) { 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; } - $files = array_merge($files, self::rglob($dir . '/' . basename($pattern), $flags)); + $files = array_merge($files, self::rglob($dir . '/' . basename($pattern), $flags, $excludeFilenames)); } return $files; } + + /** + * @param string[] $files + * @param string[] $excludeFilenames + * @return string[] + */ + 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; + }); + } }