diff --git a/README.md b/README.md
index d3513c8..369684d 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,9 @@
[![Downloads total](https://img.shields.io/packagist/dt/tomasvotruba/phpstan-bodyscan.svg?style=flat-square)](https://packagist.org/packages/tomasvotruba/phpstan-bodyscan/stats)
-Do you want to get quick glimpse of new project code quality?
+* Do you want to get quick glimpse of new project code quality?
+* Do you want to know, what PHPStan level is the best for your project?
+* Do you want to know, how much errors you're facing per level to see how hard it will be to reach them?
Get error count for each PHPStan level!
@@ -10,7 +12,10 @@ Get error count for each PHPStan level!
## How does it work?
-First, we look into the project root for `phpstan.neon` file. If found, we reuse its `parameters > paths` configuration. If not, we look for defaults source code paths like `/src`, `/app`, `/tests`, etc.
+First, we look into the project root for `phpstan.neon` file.
+
+* If found, we reuse it.
+* If not, we look for defaults source code paths like `/src`, `/app`, `/tests`, etc.
Then we run PHPStan for each level from 0 to 8. We count errors and display them in a table.
@@ -53,6 +58,15 @@ To get errors count per level:
+## Do you want to run levels without extensions?
+
+```bash
+vendor/bin/phpstan-bodyscan --bare
+```
+
+
+
+
## Do you need a JSON format?
We got you covered:
diff --git a/ecs.php b/ecs.php
index d2e270f..54010c0 100644
--- a/ecs.php
+++ b/ecs.php
@@ -1,17 +1,12 @@
withPaths([
- __DIR__ . '/bin',
- __DIR__ . '/src',
- __DIR__ . '/tests',
- ])
- ->withRules([
- LineLengthFixer::class,
- ])
+ ->withPaths([__DIR__ . '/bin', __DIR__ . '/src', __DIR__ . '/tests'])
+ ->withRootFiles()
+ ->withRules([LineLengthFixer::class])
->withPreparedSets(psr12: true, common: true, symplify: true);
diff --git a/phpstan-bodyscan.neon b/phpstan-bodyscan.neon
new file mode 100644
index 0000000..4bd6713
--- /dev/null
+++ b/phpstan-bodyscan.neon
@@ -0,0 +1,29 @@
+includes:
+ - vendor/symplify/phpstan-rules/config/code-complexity-rules.neon
+ - vendor/symplify/phpstan-rules/config/collector-rules.neon
+ - vendor/symplify/phpstan-rules/config/naming-rules.neon
+ - vendor/symplify/phpstan-rules/config/regex-rules.neon
+ - vendor/symplify/phpstan-rules/config/static-rules.neon
+
+parameters:
+ level: 8
+ excludePaths:
+ - */Fixture/*
+
+ paths:
+ - bin
+ - src
+ - tests
+
+ unused_public:
+ methods: true
+ constants: true
+ properties: true
+
+ type_coverage:
+ param: 99
+ property: 99
+ return: 99
+ declare: 99
+
+ reportUnmatchedIgnoredErrors: false
diff --git a/rector.php b/rector.php
index e677893..e874621 100644
--- a/rector.php
+++ b/rector.php
@@ -5,13 +5,15 @@
use Rector\Config\RectorConfig;
return RectorConfig::configure()
- ->withPaths([
- __DIR__ . '/bin',
- __DIR__ . '/src',
- __DIR__ . '/tests',
- ])
+ ->withPaths([__DIR__ . '/bin', __DIR__ . '/src', __DIR__ . '/tests'])
->withPreparedSets(
- deadCode: true, naming: true, privatization: true, earlyReturn: true, codeQuality: true, codingStyle: true, typeDeclarations: true
+ deadCode: true,
+ naming: true,
+ privatization: true,
+ earlyReturn: true,
+ codeQuality: true,
+ codingStyle: true,
+ typeDeclarations: true
)
->withPhpSets()
->withRootFiles()
diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php
index 95dc9ca..38ed0ea 100644
--- a/src/Command/RunCommand.php
+++ b/src/Command/RunCommand.php
@@ -7,6 +7,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TomasVotruba\PHPStanBodyscan\Logger;
@@ -22,6 +23,11 @@
final class RunCommand extends Command
{
+ /**
+ * @var array
+ */
+ private const DOT_STATES = [' ', '. ', '.. ', '...', '....', '.....'];
+
public function __construct(
private readonly SymfonyStyle $symfonyStyle,
private readonly AnalyseProcessFactory $analyseProcessFactory,
@@ -51,9 +57,12 @@ protected function configure(): void
InputOption::VALUE_NONE,
'Without any extensions, without ignores, without baselines, just pure PHPStan'
);
+
+ // @todo nobaseline - without ignores and baseline files
}
/**
+ * @param ConsoleOutput $output
* @return Command::*
*/
protected function execute(InputInterface $input, OutputInterface $output): int
@@ -61,7 +70,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/** @var string $projectDirectory */
$projectDirectory = getcwd();
- $minPhpStanLevel = (int) $input->getOption('min-level');
+ $minPhpStanLevel = $input->getOption('min-level');
$maxPhpStanLevel = (int) $input->getOption('max-level');
Assert::lessThanEq($minPhpStanLevel, $maxPhpStanLevel);
@@ -77,8 +86,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// 1. prepare empty phpstan config
// no baselines, ignores etc. etc :)
- $phpstanConfiguration = $this->phpStanConfigFactory->create($projectDirectory, $isBare);
- file_put_contents($projectDirectory . '/phpstan-bodyscan.neon', $phpstanConfiguration);
+ $phpstanConfig = $this->phpStanConfigFactory->create($projectDirectory, [], $isBare);
+ file_put_contents($projectDirectory . '/phpstan-bodyscan.neon', $phpstanConfig->getFileContents());
$levelResults = [];
@@ -94,21 +103,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// 2. measure phpstan levels
for ($phpStanLevel = $minPhpStanLevel; $phpStanLevel <= $maxPhpStanLevel; ++$phpStanLevel) {
- $this->symfonyStyle->section(sprintf('Running PHPStan level %d %s', $phpStanLevel, $withExtensions ?
- 'with extensions' : 'without extensions'));
+ $infoMessage = '' . sprintf('Running PHPStan level %d%s', $phpStanLevel, $isBare ?
+ ' without extensions' : '') . '';
+
+ $section = $output->section();
+
+ for ($i = 0; $i < 20; $i++) {
+ $stateIndex = $i % count(self::DOT_STATES);
+ $section->overwrite($infoMessage . ': ' . self::DOT_STATES[$stateIndex]);
+ usleep(700_000);
+ }
$levelResult = $this->measureErrorCountInLevel($phpStanLevel, $projectDirectory, $envVariables);
$levelResults[] = $levelResult;
- $this->symfonyStyle->writeln(sprintf(' * Found %d errors', $levelResult->getErrorCount()));
- $this->symfonyStyle->newLine();
+ $section->overwrite(sprintf($infoMessage . ': found %d errors', $levelResult->getErrorCount()));
}
- if ($withExtensions === false) {
+ if ($isBare) {
+ // restore PHPStan extension file
$this->symfonyStyle->writeln('Restoring PHPStan extensions...');
$this->symfonyStyle->newLine();
- // restore PHPStan extension file
$phpstanExtensionFile = $projectDirectory . '/vendor/phpstan/extension-installer/src/GeneratedConfig.php';
rename($phpstanExtensionFile . '.bak', $phpstanExtensionFile);
}
diff --git a/src/PHPStanConfigFactory.php b/src/PHPStanConfigFactory.php
index 2cc75a3..a2bcf15 100644
--- a/src/PHPStanConfigFactory.php
+++ b/src/PHPStanConfigFactory.php
@@ -20,7 +20,7 @@ final class PHPStanConfigFactory
/**
* @var string[]
*/
- private const PHPSTAN_FILE_NAMES = ['phpstan.neon', 'phpstan.neon.dist', 'phpstan.php', 'phpstan.php.dist'];
+ private const PHPSTAN_FILE_NAMES = ['phpstan.neon', 'phpstan.neon.dist'];
/**
* @param array $extraConfiguration
@@ -28,7 +28,7 @@ final class PHPStanConfigFactory
public function create(string $projectDirectory, array $extraConfiguration = [], bool $bare = false): PHPStanConfig
{
$existingPHPStanFile = null;
- $phpstanFileName = null;
+
foreach (self::PHPSTAN_FILE_NAMES as $phpstanFileName) {
if (file_exists($projectDirectory . '/' . $phpstanFileName)) {
$existingPHPStanFile = $projectDirectory . '/' . $phpstanFileName;
@@ -39,20 +39,26 @@ public function create(string $projectDirectory, array $extraConfiguration = [],
// no config found? we have to create it
if ($existingPHPStanFile === null) {
$phpstanConfiguration = $this->createBasicPHPStanConfiguration($projectDirectory);
- $phpstanNeon = $this->dumpToNeon($phpstanConfiguration);
- return new PHPStanConfig($phpstanNeon, null);
+ $phpStanNeonContents = $this->dumpNeonToString($phpstanConfiguration);
+ return new PHPStanConfig($phpStanNeonContents);
}
// keep original setup
if ($bare === false) {
- return new PHPStanConfig(file_get_contents($existingPHPStanFile), $phpstanFileName);
+ $phpStanNeonContents = $this->loadFileAndMergeParameters($existingPHPStanFile, [
+ 'parameters' => [
+ // disable ignored error reporting, to make no fatal errors
+ 'reportUnmatchedIgnoredErrors' => false,
+ ],
+ ]);
+ return new PHPStanConfig($phpStanNeonContents);
}
$phpstanConfiguration = $this->createBarePHPStanConfiguration($existingPHPStanFile);
$phpstanConfiguration = array_merge_recursive($phpstanConfiguration, $extraConfiguration);
- $phpstanNeon = $this->dumpToNeon($phpstanConfiguration);
- return new PHPStanConfig($phpstanNeon, $phpstanFileName);
+ $phpstanNeon = $this->dumpNeonToString($phpstanConfiguration);
+ return new PHPStanConfig($phpstanNeon);
}
/**
@@ -93,7 +99,21 @@ private function createBasicPHPStanConfiguration(string $projectDirectory): arra
];
}
- private function dumpToNeon(array $phpstanConfiguration): string
+ /**
+ * @param array $extraContents
+ */
+ private function loadFileAndMergeParameters(string $existingPHPStanFile, array $extraContents): string
+ {
+ $neon = Neon::decodeFile($existingPHPStanFile);
+ $neon = array_merge_recursive($neon, $extraContents);
+
+ return $this->dumpNeonToString($neon);
+ }
+
+ /**
+ * @param array $phpstanConfiguration
+ */
+ private function dumpNeonToString(array $phpstanConfiguration): string
{
$encodedNeon = Neon::encode($phpstanConfiguration, true, ' ');
return trim($encodedNeon) . PHP_EOL;
diff --git a/src/ValueObject/PHPStanConfig.php b/src/ValueObject/PHPStanConfig.php
index d289fbd..bfbdc13 100644
--- a/src/ValueObject/PHPStanConfig.php
+++ b/src/ValueObject/PHPStanConfig.php
@@ -8,7 +8,6 @@
{
public function __construct(
private string $fileContents,
- private ?string $originalFileName
) {
}
@@ -16,9 +15,4 @@ public function getFileContents(): string
{
return $this->fileContents;
}
-
- public function getOriginalFileName(): ?string
- {
- return $this->originalFileName;
- }
}
diff --git a/tests/PHPStanConfigFactory/Fixture/expected-some-project-phpstan.neon b/tests/PHPStanConfigFactory/Fixture/expected-some-project-phpstan.neon
index 9d5c032..57a429d 100644
--- a/tests/PHPStanConfigFactory/Fixture/expected-some-project-phpstan.neon
+++ b/tests/PHPStanConfigFactory/Fixture/expected-some-project-phpstan.neon
@@ -5,3 +5,5 @@ parameters:
excludePaths:
- src/SomeOther
+
+ reportUnmatchedIgnoredErrors: false
diff --git a/tests/PHPStanConfigFactory/PHPStanConfigFactoryTest.php b/tests/PHPStanConfigFactory/PHPStanConfigFactoryTest.php
index f0a5dbc..a34595f 100644
--- a/tests/PHPStanConfigFactory/PHPStanConfigFactoryTest.php
+++ b/tests/PHPStanConfigFactory/PHPStanConfigFactoryTest.php
@@ -21,9 +21,8 @@ protected function setUp(): void
#[DataProvider('provideData')]
public function test(string $projectDirectory, string $expectedPHPStanConfigFile): void
{
- $phpstanNeon = $this->phpStanConfigFactory->create($projectDirectory);
-
- $this->assertStringEqualsFile($expectedPHPStanConfigFile, $phpstanNeon->getFileContents());
+ $phpStanConfig = $this->phpStanConfigFactory->create($projectDirectory);
+ $this->assertStringEqualsFile($expectedPHPStanConfigFile, $phpStanConfig->getFileContents());
}
public static function provideData(): Iterator