Skip to content

Commit

Permalink
add --bare
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed May 31, 2024
1 parent e30a0a3 commit 997c199
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 43 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

[![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!

<br>

## 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.

Expand Down Expand Up @@ -53,6 +58,15 @@ To get errors count per level:

<br>

## Do you want to run levels without extensions?

```bash
vendor/bin/phpstan-bodyscan --bare
```

<br>


## Do you need a JSON format?

We got you covered:
Expand Down
13 changes: 4 additions & 9 deletions ecs.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
<?php

declare(strict_types=1);
use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;

use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;

return ECSConfig::configure()
->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);
29 changes: 29 additions & 0 deletions phpstan-bodyscan.neon
Original file line number Diff line number Diff line change
@@ -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
14 changes: 8 additions & 6 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
34 changes: 25 additions & 9 deletions src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,6 +23,11 @@

final class RunCommand extends Command
{
/**
* @var array<int, string>
*/
private const DOT_STATES = [' ', '. ', '.. ', '...', '....', '.....'];

public function __construct(
private readonly SymfonyStyle $symfonyStyle,
private readonly AnalyseProcessFactory $analyseProcessFactory,
Expand Down Expand Up @@ -51,17 +57,20 @@ 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
{
/** @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);

Expand All @@ -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 = [];

Expand All @@ -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 = '<info>' . sprintf('Running PHPStan level %d%s', $phpStanLevel, $isBare ?
' without extensions' : '') . '</info>';

$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(' * <info>Found %d errors</info>', $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);
}
Expand Down
36 changes: 28 additions & 8 deletions src/PHPStanConfigFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ 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<string, mixed[]> $extraConfiguration
*/
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;
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -93,7 +99,21 @@ private function createBasicPHPStanConfiguration(string $projectDirectory): arra
];
}

private function dumpToNeon(array $phpstanConfiguration): string
/**
* @param array<string, mixed> $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<string, mixed> $phpstanConfiguration
*/
private function dumpNeonToString(array $phpstanConfiguration): string
{
$encodedNeon = Neon::encode($phpstanConfiguration, true, ' ');
return trim($encodedNeon) . PHP_EOL;
Expand Down
6 changes: 0 additions & 6 deletions src/ValueObject/PHPStanConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,11 @@
{
public function __construct(
private string $fileContents,
private ?string $originalFileName
) {
}

public function getFileContents(): string
{
return $this->fileContents;
}

public function getOriginalFileName(): ?string
{
return $this->originalFileName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ parameters:

excludePaths:
- src/SomeOther

reportUnmatchedIgnoredErrors: false
5 changes: 2 additions & 3 deletions tests/PHPStanConfigFactory/PHPStanConfigFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 997c199

Please sign in to comment.