Skip to content

Commit 8260032

Browse files
committed
Allow generating baseline in .php format
1 parent 34881e6 commit 8260032

File tree

6 files changed

+135
-8
lines changed

6 files changed

+135
-8
lines changed

Diff for: .github/workflows/static-analysis.yml

+26
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,29 @@ jobs:
173173
cp phpstan-baseline.neon phpstan-baseline-orig.neon && \
174174
make phpstan-generate-baseline && \
175175
diff phpstan-baseline.neon phpstan-baseline-orig.neon
176+
177+
generate-baseline-php:
178+
name: "Generate PHP baseline"
179+
180+
runs-on: "ubuntu-latest"
181+
timeout-minutes: 60
182+
183+
steps:
184+
- name: "Checkout"
185+
uses: actions/checkout@v3
186+
187+
- name: "Install PHP"
188+
uses: "shivammathur/setup-php@v2"
189+
with:
190+
coverage: "none"
191+
php-version: "8.1"
192+
ini-file: development
193+
194+
- name: "Install dependencies"
195+
run: "composer install --no-interaction --no-progress"
196+
197+
- name: "Generate baseline"
198+
run: |
199+
> phpstan-baseline.neon && \
200+
make phpstan-generate-baseline-php && \
201+
make phpstan-result-cache

Diff for: Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ phpstan-result-cache:
6262
phpstan-generate-baseline:
6363
php -d memory_limit=448M bin/phpstan --generate-baseline
6464

65+
phpstan-generate-baseline-php:
66+
php -d memory_limit=448M bin/phpstan analyse --generate-baseline phpstan-baseline.php
67+
6568
phpstan-pro:
6669
php -d memory_limit=448M bin/phpstan --pro
6770

Diff for: build/phpstan.neon

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ includes:
77
- ../vendor/phpstan/phpstan-strict-rules/rules.neon
88
- ../conf/bleedingEdge.neon
99
- ../phpstan-baseline.neon
10+
- ../phpstan-baseline.php
1011
- ignore-by-php-version.neon.php
1112
- ignore-by-architecture.neon.php
1213
parameters:

Diff for: phpstan-baseline.php

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php declare(strict_types = 1);
2+
3+
return [];

Diff for: src/Command/AnalyseCommand.php

+15-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use OndraM\CiDetector\CiDetector;
66
use PHPStan\Command\ErrorFormatter\BaselineNeonErrorFormatter;
7+
use PHPStan\Command\ErrorFormatter\BaselinePhpErrorFormatter;
78
use PHPStan\Command\ErrorFormatter\ErrorFormatter;
89
use PHPStan\Command\ErrorFormatter\TableErrorFormatter;
910
use PHPStan\Command\Symfony\SymfonyOutput;
@@ -28,6 +29,7 @@
2829
use function fopen;
2930
use function get_class;
3031
use function implode;
32+
use function in_array;
3133
use function is_array;
3234
use function is_bool;
3335
use function is_dir;
@@ -202,8 +204,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
202204
return $inceptionResult->handleReturn(1, null);
203205
}
204206

205-
if ($baselineExtension !== 'neon') {
206-
$inceptionResult->getStdOutput()->getStyle()->error(sprintf('Baseline filename extension must be .neon, .%s was used instead.', $baselineExtension));
207+
if (!in_array($baselineExtension, ['neon', 'php'], true)) {
208+
$inceptionResult->getStdOutput()->getStyle()->error(sprintf('Baseline filename extension must be .neon or .php, .%s was used instead.', $baselineExtension));
207209

208210
return $inceptionResult->handleReturn(1, null);
209211
}
@@ -292,15 +294,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
292294
return $inceptionResult->handleReturn(1, $analysisResult->getPeakMemoryUsageBytes());
293295
}
294296

295-
$baselineFileDirectory = dirname($generateBaselineFile);
296-
$baselineErrorFormatter = new BaselineNeonErrorFormatter(new ParentDirectoryRelativePathHelper($baselineFileDirectory));
297-
298-
$existingBaselineContent = is_file($generateBaselineFile) ? FileReader::read($generateBaselineFile) : '';
299-
300297
$streamOutput = $this->createStreamOutput();
301298
$errorConsoleStyle = new ErrorsConsoleStyle(new StringInput(''), $streamOutput);
302299
$baselineOutput = new SymfonyOutput($streamOutput, new SymfonyStyle($errorConsoleStyle));
303-
$baselineErrorFormatter->formatErrors($analysisResult, $baselineOutput, $existingBaselineContent);
300+
$baselineFileDirectory = dirname($generateBaselineFile);
301+
$baselinePathHelper = new ParentDirectoryRelativePathHelper($baselineFileDirectory);
302+
303+
if ($baselineExtension === 'php') {
304+
$baselineErrorFormatter = new BaselinePhpErrorFormatter($baselinePathHelper);
305+
$baselineErrorFormatter->formatErrors($analysisResult, $baselineOutput);
306+
} else {
307+
$baselineErrorFormatter = new BaselineNeonErrorFormatter($baselinePathHelper);
308+
$existingBaselineContent = is_file($generateBaselineFile) ? FileReader::read($generateBaselineFile) : '';
309+
$baselineErrorFormatter->formatErrors($analysisResult, $baselineOutput, $existingBaselineContent);
310+
}
304311

305312
$stream = $streamOutput->getStream();
306313
rewind($stream);
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Command\ErrorFormatter;
4+
5+
use Nette\DI\Helpers;
6+
use PHPStan\Command\AnalysisResult;
7+
use PHPStan\Command\Output;
8+
use PHPStan\File\RelativePathHelper;
9+
use function ksort;
10+
use function preg_quote;
11+
use function sprintf;
12+
use function var_export;
13+
use const SORT_STRING;
14+
15+
class BaselinePhpErrorFormatter
16+
{
17+
18+
public function __construct(private RelativePathHelper $relativePathHelper)
19+
{
20+
}
21+
22+
public function formatErrors(
23+
AnalysisResult $analysisResult,
24+
Output $output,
25+
): int
26+
{
27+
if (!$analysisResult->hasErrors()) {
28+
$php = '<?php declare(strict_types = 1);';
29+
$php .= "\n\n";
30+
$php .= 'return [];';
31+
$php .= "\n";
32+
$output->writeRaw($php);
33+
return 0;
34+
}
35+
36+
$fileErrors = [];
37+
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
38+
if (!$fileSpecificError->canBeIgnored()) {
39+
continue;
40+
}
41+
$fileErrors['/' . $this->relativePathHelper->getRelativePath($fileSpecificError->getFilePath())][] = $fileSpecificError->getMessage();
42+
}
43+
ksort($fileErrors, SORT_STRING);
44+
45+
$php = '<?php declare(strict_types = 1);';
46+
$php .= "\n\n";
47+
$php .= '$ignoreErrors = [];';
48+
$php .= "\n";
49+
foreach ($fileErrors as $file => $errorMessages) {
50+
$fileErrorsCounts = [];
51+
foreach ($errorMessages as $errorMessage) {
52+
if (!isset($fileErrorsCounts[$errorMessage])) {
53+
$fileErrorsCounts[$errorMessage] = 1;
54+
continue;
55+
}
56+
57+
$fileErrorsCounts[$errorMessage]++;
58+
}
59+
ksort($fileErrorsCounts, SORT_STRING);
60+
61+
foreach ($fileErrorsCounts as $message => $count) {
62+
$template = <<<'PHP'
63+
$ignoreErrors[] = [
64+
'message' => %s,
65+
'count' => %d,
66+
'path' => __DIR__ . %s,
67+
];
68+
PHP;
69+
$php .= sprintf(
70+
$template,
71+
var_export(Helpers::escape('#^' . preg_quote($message, '#') . '$#'), true),
72+
var_export($count, true),
73+
var_export(Helpers::escape($file), true),
74+
);
75+
$php .= "\n";
76+
}
77+
}
78+
79+
$php .= "\n";
80+
$php .= 'return [\'parameters\' => [\'ignoreErrors\' => $ignoreErrors]];';
81+
82+
$output->writeRaw($php);
83+
84+
return 1;
85+
}
86+
87+
}

0 commit comments

Comments
 (0)