Skip to content

Commit

Permalink
add PHPStan neon value object, add --bare option to use only paths/ex…
Browse files Browse the repository at this point in the history
…cludes from original config
  • Loading branch information
TomasVotruba committed May 31, 2024
1 parent a75055b commit e30a0a3
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 35 deletions.
11 changes: 5 additions & 6 deletions src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ protected function configure(): void
$this->addOption('json', null, InputOption::VALUE_NONE, 'Show result in JSON');

$this->addOption(
'with-extensions',
'bare',
null,
InputOption::VALUE_NONE,
'Enable PHPStan extensions (removed by default)'
'Without any extensions, without ignores, without baselines, just pure PHPStan'
);
}

Expand All @@ -65,8 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$maxPhpStanLevel = (int) $input->getOption('max-level');
Assert::lessThanEq($minPhpStanLevel, $maxPhpStanLevel);

$withExtensions = (bool) $input->getOption('with-extensions');

$isBare = (bool) $input->getOption('bare');
$isJson = (bool) $input->getOption('json');

// silence output till the end to avoid invalid json format
Expand All @@ -78,12 +77,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int

// 1. prepare empty phpstan config
// no baselines, ignores etc. etc :)
$phpstanConfiguration = $this->phpStanConfigFactory->create($projectDirectory);
$phpstanConfiguration = $this->phpStanConfigFactory->create($projectDirectory, $isBare);
file_put_contents($projectDirectory . '/phpstan-bodyscan.neon', $phpstanConfiguration);

$levelResults = [];

if ($withExtensions === false) {
if ($isBare) {
// temporarily disable project PHPStan extensions
$phpstanExtensionFile = $projectDirectory . '/vendor/phpstan/extension-installer/src/GeneratedConfig.php';
if (file_exists($phpstanExtensionFile)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Command/TypeCoverageCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private function measureTypeCoverage(string $projectDirectory): TypeCoverageResu
],
'customRulesetUsed' => true,
],
]);
], true);
file_put_contents($projectDirectory . '/phpstan-bodyscan.neon', $phpstanConfiguration);

$process = $this->analyseProcessFactory->createTypeCoverageProcess($projectDirectory);
Expand Down
83 changes: 57 additions & 26 deletions src/PHPStanConfigFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace TomasVotruba\PHPStanBodyscan;

use Nette\Neon\Neon;
use TomasVotruba\PHPStanBodyscan\ValueObject\PHPStanConfig;

/**
* @see \TomasVotruba\PHPStanBodyscan\Tests\PHPStanConfigFactory\PHPStanConfigFactoryTest
Expand All @@ -16,47 +17,49 @@ final class PHPStanConfigFactory
*/
private const POSSIBLE_SOURCE_PATHS = ['app', 'config', 'lib', 'src', 'tests'];

/**
* @var string[]
*/
private const PHPSTAN_FILE_NAMES = ['phpstan.neon', 'phpstan.neon.dist', 'phpstan.php', 'phpstan.php.dist'];

/**
* @param array<string, mixed[]> $extraConfiguration
*/
public function create(string $projectDirectory, array $extraConfiguration = []): string
public function create(string $projectDirectory, array $extraConfiguration = [], bool $bare = false): PHPStanConfig
{
$projectPHPStanFile = $projectDirectory . '/phpstan.neon';
$existingPHPStanFile = null;
$phpstanFileName = null;
foreach (self::PHPSTAN_FILE_NAMES as $phpstanFileName) {
if (file_exists($projectDirectory . '/' . $phpstanFileName)) {
$existingPHPStanFile = $projectDirectory . '/' . $phpstanFileName;
break;
}
}

// no config found? we have to create it
if ($existingPHPStanFile === null) {
$phpstanConfiguration = $this->createBasicPHPStanConfiguration($projectDirectory);
$phpstanNeon = $this->dumpToNeon($phpstanConfiguration);
return new PHPStanConfig($phpstanNeon, null);
}

if (! file_exists($projectPHPStanFile)) {
// add fallback to dist file
$projectPHPStanFile = $projectDirectory . '/phpstan.neon.dist';
// keep original setup
if ($bare === false) {
return new PHPStanConfig(file_get_contents($existingPHPStanFile), $phpstanFileName);
}

$phpstanConfiguration = $this->resolvePHPStanConfiguration($projectPHPStanFile, $projectDirectory);
$phpstanConfiguration = $this->createBarePHPStanConfiguration($existingPHPStanFile);
$phpstanConfiguration = array_merge_recursive($phpstanConfiguration, $extraConfiguration);

$encodedNeon = Neon::encode($phpstanConfiguration, true, ' ');
return trim($encodedNeon) . PHP_EOL;
$phpstanNeon = $this->dumpToNeon($phpstanConfiguration);
return new PHPStanConfig($phpstanNeon, $phpstanFileName);
}

/**
* @return mixed[]
*/
private function resolvePHPStanConfiguration(string $projectPHPStanFile, string $projectDirectory): array
private function createBarePHPStanConfiguration(string $projectPHPStanFile): array
{
if (! file_exists($projectPHPStanFile)) {
$sourcePaths = array_filter(
self::POSSIBLE_SOURCE_PATHS,
static fn (string $possibleSourcePath): bool => file_exists(
$projectDirectory . '/' . $possibleSourcePath
)
);

$sourcePaths = array_values($sourcePaths);

return [
'parameters' => [
'paths' => $sourcePaths,
],
];
}

// make use of existing PHPStan paths
$projectPHPStan = Neon::decodeFile($projectPHPStanFile);

Expand All @@ -67,4 +70,32 @@ private function resolvePHPStanConfiguration(string $projectPHPStanFile, string
],
];
}

/**
* @return array<string, mixed>
*/
private function createBasicPHPStanConfiguration(string $projectDirectory): array
{
// in case of no config
$sourcePaths = array_filter(
self::POSSIBLE_SOURCE_PATHS,
static fn (string $possibleSourcePath): bool => file_exists(
$projectDirectory . '/' . $possibleSourcePath
)
);

$sourcePaths = array_values($sourcePaths);

return [
'parameters' => [
'paths' => $sourcePaths,
],
];
}

private function dumpToNeon(array $phpstanConfiguration): string
{
$encodedNeon = Neon::encode($phpstanConfiguration, true, ' ');
return trim($encodedNeon) . PHP_EOL;
}
}
24 changes: 24 additions & 0 deletions src/ValueObject/PHPStanConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace TomasVotruba\PHPStanBodyscan\ValueObject;

final readonly class PHPStanConfig
{
public function __construct(
private string $fileContents,
private ?string $originalFileName
) {
}

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

public function getOriginalFileName(): ?string
{
return $this->originalFileName;
}
}
4 changes: 2 additions & 2 deletions tests/PHPStanConfigFactory/PHPStanConfigFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ protected function setUp(): void
#[DataProvider('provideData')]
public function test(string $projectDirectory, string $expectedPHPStanConfigFile): void
{
$phpStanBodyscanConfig = $this->phpStanConfigFactory->create($projectDirectory);
$phpstanNeon = $this->phpStanConfigFactory->create($projectDirectory);

$this->assertStringEqualsFile($expectedPHPStanConfigFile, $phpStanBodyscanConfig);
$this->assertStringEqualsFile($expectedPHPStanConfigFile, $phpstanNeon->getFileContents());
}

public static function provideData(): Iterator
Expand Down

0 comments on commit e30a0a3

Please sign in to comment.