Skip to content

Commit

Permalink
add json output
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed May 20, 2024
1 parent c58c597 commit c10fcf1
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 51 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ vendor/bin/phpstan-bodyscan --json
},
{
"level": 1,
"error_count": 0
"error_count": 5
},
{
"level": 2,
"error_count": 0
"error_count": 25
}
]
```
Expand Down
14 changes: 13 additions & 1 deletion bin/phpstan-bodyscan.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Style\SymfonyStyle;
use TomasVotruba\PHPStanBodyscan\Command\RunCommand;
use TomasVotruba\PHPStanBodyscan\OutputFormatter\JsonOutputFormatter;
use TomasVotruba\PHPStanBodyscan\OutputFormatter\TableOutputFormatter;
use TomasVotruba\PHPStanBodyscan\PHPStanConfigFactory;
use TomasVotruba\PHPStanBodyscan\Process\AnalyseProcessFactory;

Expand All @@ -21,7 +23,17 @@

// 1. setup dependencies
$symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput());
$runCommand = new RunCommand($symfonyStyle, new AnalyseProcessFactory(), new PHPStanConfigFactory());

$jsonOutputFormatter = new JsonOutputFormatter($symfonyStyle);
$tableOutputFormatter = new TableOutputFormatter($symfonyStyle);

$runCommand = new RunCommand(
$symfonyStyle,
new AnalyseProcessFactory(),
new PHPStanConfigFactory(),
$jsonOutputFormatter,
$tableOutputFormatter
);

$application = new Application();
$application->add($runCommand);
Expand Down
62 changes: 15 additions & 47 deletions src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace TomasVotruba\PHPStanBodyscan\Command;

use Nette\Utils\Json;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -15,19 +13,24 @@
use Symfony\Component\Process\Process;
use TomasVotruba\PHPStanBodyscan\Exception\AnalysisFailedException;
use TomasVotruba\PHPStanBodyscan\Logger;
use TomasVotruba\PHPStanBodyscan\OutputFormatter\JsonOutputFormatter;
use TomasVotruba\PHPStanBodyscan\OutputFormatter\TableOutputFormatter;
use TomasVotruba\PHPStanBodyscan\PHPStanConfigFactory;
use TomasVotruba\PHPStanBodyscan\Process\AnalyseProcessFactory;
use TomasVotruba\PHPStanBodyscan\Utils\ComposerLoader;
use TomasVotruba\PHPStanBodyscan\Utils\FileLoader;
use TomasVotruba\PHPStanBodyscan\Utils\JsonLoader;
use TomasVotruba\PHPStanBodyscan\ValueObject\PHPStanLevelResult;
use TomasVotruba\PHPStanBodyscan\ValueObject\BodyscanResult;
use TomasVotruba\PHPStanBodyscan\ValueObject\LevelResult;

final class RunCommand extends Command
{
public function __construct(
private readonly SymfonyStyle $symfonyStyle,
private readonly AnalyseProcessFactory $analyseProcessFactory,
private readonly PHPStanConfigFactory $phpStanConfigFactory
private readonly PHPStanConfigFactory $phpStanConfigFactory,
private readonly JsonOutputFormatter $jsonOutputFormatter,
private readonly TableOutputFormatter $tableOutputFormatter,
) {
parent::__construct();
}
Expand Down Expand Up @@ -64,7 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$envVariables = $this->loadEnvVariables($input);

$phpStanLevelResults = [];
$levelResults = [];

// 1. prepare empty phpstan config
// no baselines, ignores etc. etc :)
Expand All @@ -75,31 +78,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
for ($phpStanLevel = $minPhpStanLevel; $phpStanLevel <= $maxPhpStanLevel; ++$phpStanLevel) {
$this->symfonyStyle->section(sprintf('Running PHPStan level %d', $phpStanLevel));

$phpStanLevelResults[] = $this->measureErrorCountInLevel($phpStanLevel, $projectDirectory, $envVariables);
$levelResults[] = $this->measureErrorCountInLevel($phpStanLevel, $projectDirectory, $envVariables);

$this->symfonyStyle->newLine();
}

$bodyscanResult = new BodyscanResult($levelResults);

// 3. tidy up temporary config
unlink($projectDirectory . '/phpstan-bodyscan.neon');

if ($isJson) {
// restore verbosity
$this->symfonyStyle->setVerbosity(OutputInterface::VERBOSITY_NORMAL);

$rawData = [];
/** @var PHPStanLevelResult[] $phpStanLevelResults */
foreach ($phpStanLevelResults as $phpStanLevelResult) {
$rawData[] = [
'level' => $phpStanLevelResult->getLevel(),
'error_count' => $phpStanLevelResult->getErrorCount(),
];
}

$jsonOutput = json_encode($rawData, JSON_PRETTY_PRINT);
$this->symfonyStyle->writeln($jsonOutput);
$this->jsonOutputFormatter->outputResult($bodyscanResult);
} else {
$this->renderResultInTable($phpStanLevelResults);
$this->tableOutputFormatter->outputResult($bodyscanResult);
}

return self::SUCCESS;
Expand All @@ -112,7 +104,7 @@ private function measureErrorCountInLevel(
int $phpStanLevel,
string $projectDirectory,
array $envVariables
): PHPStanLevelResult {
): LevelResult {
$analyseLevelProcess = $this->analyseProcessFactory->create($projectDirectory, $phpStanLevel, $envVariables);

$this->symfonyStyle->writeln('Running: <fg=green>' . $analyseLevelProcess->getCommandLine() . '</>');
Expand Down Expand Up @@ -147,31 +139,7 @@ private function measureErrorCountInLevel(
$fileErrorCount
));

return new PHPStanLevelResult($phpStanLevel, $fileErrorCount);
}

/**
* @param PHPStanLevelResult[] $phpStanLevelResults
*/
private function renderResultInTable(array $phpStanLevelResults): void
{
// convert to symfony table data
$tableRows = [];
foreach ($phpStanLevelResults as $phpStanLevelResult) {
$tableRows[] = [$phpStanLevelResult->getLevel(), $phpStanLevelResult->getErrorCount()];
}

$tableStyle = new TableStyle();
$tableStyle->setPadType(STR_PAD_LEFT);

$this->symfonyStyle->newLine(2);

$this->symfonyStyle->createTable()
->setHeaders(['Level', 'Error count'])
->setRows($tableRows)
// align right
->setStyle($tableStyle)
->render();
return new LevelResult($phpStanLevel, $fileErrorCount);
}

private function ensurePHPStanIsInstalled(string $projectDirectory, string $vendorBinDirectory): void
Expand Down
12 changes: 12 additions & 0 deletions src/Contract/OutputFormatterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace TomasVotruba\PHPStanBodyscan\Contract;

use TomasVotruba\PHPStanBodyscan\ValueObject\BodyscanResult;

interface OutputFormatterInterface
{
public function outputResult(BodyscanResult $bodyscanResult): void;
}
36 changes: 36 additions & 0 deletions src/OutputFormatter/JsonOutputFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace TomasVotruba\PHPStanBodyscan\OutputFormatter;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TomasVotruba\PHPStanBodyscan\Contract\OutputFormatterInterface;
use TomasVotruba\PHPStanBodyscan\ValueObject\BodyscanResult;

final readonly class JsonOutputFormatter implements OutputFormatterInterface
{
public function __construct(
private SymfonyStyle $symfonyStyle
) {
}

public function outputResult(BodyscanResult $bodyscanResult): void
{
// restore verbosity
$this->symfonyStyle->setVerbosity(OutputInterface::VERBOSITY_NORMAL);

$rawData = [];

foreach ($bodyscanResult->getLevelResults() as $levelResult) {
$rawData[] = [
'level' => $levelResult->getLevel(),
'error_count' => $levelResult->getErrorCount(),
];
}

$jsonOutput = json_encode($rawData, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
$this->symfonyStyle->writeln($jsonOutput);
}
}
39 changes: 39 additions & 0 deletions src/OutputFormatter/TableOutputFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace TomasVotruba\PHPStanBodyscan\OutputFormatter;

use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Style\SymfonyStyle;
use TomasVotruba\PHPStanBodyscan\Contract\OutputFormatterInterface;
use TomasVotruba\PHPStanBodyscan\ValueObject\BodyscanResult;

final readonly class TableOutputFormatter implements OutputFormatterInterface
{
public function __construct(
private SymfonyStyle $symfonyStyle,
) {
}

public function outputResult(BodyscanResult $bodyscanResult): void
{
// convert to symfony table data
$tableRows = [];
foreach ($bodyscanResult->getLevelResults() as $levelResult) {
$tableRows[] = [$levelResult->getLevel(), $levelResult->getErrorCount()];
}

$tableStyle = new TableStyle();
$tableStyle->setPadType(STR_PAD_LEFT);

$this->symfonyStyle->newLine(2);

$this->symfonyStyle->createTable()
->setHeaders(['Level', 'Error count'])
->setRows($tableRows)
// align right
->setStyle($tableStyle)
->render();
}
}
24 changes: 24 additions & 0 deletions src/ValueObject/BodyscanResult.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 BodyscanResult
{
/**
* @param LevelResult[] $levelResults
*/
public function __construct(
private array $levelResults,
) {
}

/**
* @return LevelResult[]
*/
public function getLevelResults(): array
{
return $this->levelResults;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace TomasVotruba\PHPStanBodyscan\ValueObject;

final readonly class PHPStanLevelResult
final readonly class LevelResult
{
public function __construct(
private int $level,
Expand Down

0 comments on commit c10fcf1

Please sign in to comment.