Skip to content

Commit

Permalink
Introduces new component ConfigResolver #166 and fix issue #163 (back…
Browse files Browse the repository at this point in the history
…port from branch 8.0)
  • Loading branch information
llaville committed Dec 22, 2022
1 parent 32208f2 commit c633e8a
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 101 deletions.
151 changes: 50 additions & 101 deletions src/Command/LintCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
namespace Overtrue\PHPLint\Command;

use DateTime;
use Overtrue\PHPLint\Configuration\ConfigResolver;
use Overtrue\PHPLint\Cache;
use Overtrue\PHPLint\Linter;
use PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor;
use PHP_Parallel_Lint\PhpConsoleColor\InvalidStyleException;
use PHP_Parallel_Lint\PhpConsoleHighlighter\Highlighter;
use N98\JUnitXml\Document;
use Overtrue\PHPLint\Cache;
use Overtrue\PHPLint\Linter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
Expand All @@ -17,23 +18,9 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;

class LintCommand extends Command
{
private const DEFAULT_EXTENSIONS = ['php'];
private const DEFAULT_JOBS = 5;
private const DEFAULT_PATH = '.';

protected array $defaults = [
'jobs' => self::DEFAULT_JOBS,
'path' => self::DEFAULT_PATH,
'exclude' => [],
'extensions' => self::DEFAULT_EXTENSIONS,
'warning' => false
];

protected InputInterface $input;
protected OutputInterface $output;

Expand All @@ -45,7 +32,7 @@ protected function configure(): void
->addArgument(
'path',
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
'Path to file or directory to lint.'
'Path to file or directory to lint'
)
->addOption(
'exclude',
Expand All @@ -58,75 +45,76 @@ protected function configure(): void
null,
InputOption::VALUE_REQUIRED,
'Check only files with selected extensions',
self::DEFAULT_EXTENSIONS
ConfigResolver::DEFAULT_EXTENSIONS
)
->addOption(
'jobs',
'j',
InputOption::VALUE_REQUIRED,
'Number of paralleled jobs to run',
self::DEFAULT_JOBS
ConfigResolver::DEFAULT_JOBS
)
->addOption(
'configuration',
'c',
InputOption::VALUE_REQUIRED,
'Read configuration from config file',
$this->getConfigFile([self::DEFAULT_PATH])
ConfigResolver::DEFAULT_CONFIG_FILE
)
->addOption(
'no-configuration',
null,
InputOption::VALUE_NONE,
'Ignore default configuration file (<comment>.phplint.yml</comment>).'
'Ignore default configuration file (<comment>.phplint.yml</comment>)'
)
->addOption(
'no-cache',
null,
InputOption::VALUE_NONE,
'Ignore cached data.'
'Ignore cached data'
)
->addOption(
'cache',
null,
InputOption::VALUE_REQUIRED,
'Path to the cache file.'
InputOption::VALUE_OPTIONAL,
'Path to the cache file',
ConfigResolver::DEFAULT_CACHE_FILE
)
->addOption(
'no-progress',
null,
InputOption::VALUE_NONE,
'Hide the progress output.'
'Hide the progress output'
)
->addOption(
'json',
null,
InputOption::VALUE_OPTIONAL,
'Path to store JSON results.'
'Path to store JSON results'
)
->addOption(
'xml',
null,
InputOption::VALUE_OPTIONAL,
'Path to store JUnit XML results.'
'Path to store JUnit XML results'
)
->addOption(
'warning',
'w',
InputOption::VALUE_NONE,
'Also show warnings.'
'Also show warnings'
)
->addOption(
'quiet',
'q',
InputOption::VALUE_NONE,
'Allow to silently fail.'
'Allow to silently fail'
)
->addOption(
'no-files-exit-code',
'nf',
null,
InputOption::VALUE_NONE,
'Throw error if no files processed.'
'Throw error if no files processed'
);
}

Expand All @@ -137,7 +125,7 @@ public function initialize(InputInterface $input, OutputInterface $output): void
}

/**
* @throws InvalidStyleException
* @throws InvalidStyleException|\Throwable
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
Expand All @@ -151,30 +139,38 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$options = $this->mergeOptions();
$verbosity = $output->getVerbosity();

if (!empty($options['configuration'])) {
$output->writeln(\sprintf('Configuration : <comment>%s</comment>', $options['configuration']));
if (!empty($options[ConfigResolver::OPTION_CONFIG_FILE])) {
$output->writeln(\sprintf('Configuration : <comment>%s</comment>', $options[ConfigResolver::OPTION_CONFIG_FILE]));
}

if ($verbosity >= OutputInterface::VERBOSITY_DEBUG) {
$output->writeln('Options : <comment>' . json_encode($options) . "</comment>\n");
$output->writeln('Options :');
foreach ($options as $name => $value) {
$output->writeln(\sprintf("<comment>%18s</comment> > <info>%s</info>", $name, \json_encode($value, \JSON_UNESCAPED_SLASHES)));
}
}
$output->writeln('');

$linter = new Linter($options['path'], $options['exclude'], $options['extensions'], $options['warning']);
$linter->setProcessLimit($options['jobs']);
$linter = new Linter(
$options[ConfigResolver::OPTION_PATH],
$options[ConfigResolver::OPTION_EXCLUDE],
$options[ConfigResolver::OPTION_EXTENSIONS],
$options[ConfigResolver::OPTION_WARNING]
);
$linter->setProcessLimit($options[ConfigResolver::OPTION_JOBS]);

if (!empty($options['cache'])) {
Cache::setFilename($options['cache']);
if (!empty($options[ConfigResolver::OPTION_CACHE_FILE])) {
Cache::setFilename($options[ConfigResolver::OPTION_CACHE_FILE]);
}

$usingCache = 'No';
if (!$input->getOption('no-cache') && Cache::isCached()) {
if (!$options[ConfigResolver::OPTION_NO_CACHE] && Cache::isCached()) {
$usingCache = 'Yes';
$linter->setCache(Cache::get());
}

if (!empty($options['memory_limit'])) {
$linter->setMemoryLimit($options['memory_limit']);
if (!empty($options[ConfigResolver::OPTION_MEMORY_LIMIT])) {
$linter->setMemoryLimit($options[ConfigResolver::OPTION_MEMORY_LIMIT]);
}

$fileCount = count($linter->getFiles());
Expand All @@ -183,7 +179,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($fileCount <= 0) {
$output->writeln('<info>Could not find files to lint</info>');

if (!empty($options['no-files-exit-code'])) {
if (!empty($options[ConfigResolver::OPTION_NO_FILES_EXIT_CODE])) {
$code = 1;
}

Expand All @@ -209,7 +205,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$output->writeln("<error>Files: {$fileCount}, Failures: {$errCount}</error>");
$this->showErrors($errors);

if (empty($options['quiet'])) {
if (empty($options[ConfigResolver::OPTION_QUIET])) {
$code = 1;
}
} else {
Expand All @@ -223,12 +219,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'files_count' => $fileCount,
];

if (!empty($options['json'])) {
$this->dumpJsonResult((string) $options['json'], $errors, $options, $context);
if (!empty($options[ConfigResolver::OPTION_JSON_FILE])) {
$this->dumpJsonResult((string) $options[ConfigResolver::OPTION_JSON_FILE], $errors, $options, $context);
}

if (!empty($options['xml'])) {
$this->dumpXmlResult((string) $options['xml'], $errors, $options, $context);
if (!empty($options[ConfigResolver::OPTION_XML_FILE])) {
$this->dumpXmlResult((string) $options[ConfigResolver::OPTION_XML_FILE], $errors, $options, $context);
}

return $code;
Expand Down Expand Up @@ -319,7 +315,7 @@ protected function showErrors(array $errors): void
foreach ($errors as $filename => $error) {
$this->output->writeln('<comment>' . ++$i . ". {$filename}:{$error['line']}" . '</comment>');

$this->output->write($this->getHighlightedCodeSnippet($filename, $error['line']));
$this->output->writeln($this->getHighlightedCodeSnippet($filename, $error['line']));

$this->output->writeln("<error> {$error['error']}</error>");
}
Expand Down Expand Up @@ -365,61 +361,14 @@ public function getHighlightedCodeSnippet(string $filePath, int $lineNumber, int

protected function mergeOptions(): array
{
$options = $this->input->getOptions();
$options['path'] = $this->input->getArgument('path');
$options['cache'] = $this->input->getOption('cache');
if ($options['warning'] === false) {
unset($options['warning']);
}

$config = [];
$configResolver = new ConfigResolver($this->input);
$options = $configResolver->resolve();
$failures = $configResolver->getNestedExceptions();

if (!$this->input->getOption('no-configuration')) {
$inputPath = $this->input->getArgument('path');
$filename = $this->getConfigFile($inputPath);

if (empty($options['configuration']) && $filename) {
$options['configuration'] = $filename;
}

if (!empty($options['configuration'])) {
$config = $this->loadConfiguration($options['configuration']);
}
if (!empty($failures)) {
throw $failures[0];
}

$options = array_merge($this->defaults, array_filter($config), array_filter($options));

is_array($options['extensions']) || $options['extensions'] = explode(',', $options['extensions']);

return $options;
}

protected function getConfigFile(array $inputPath): false|string
{
if (1 == count($inputPath) && $first = reset($inputPath)) {
$dir = is_dir($first) ? $first : dirname($first);
} else {
$dir = \getcwd() . DIRECTORY_SEPARATOR;
}

$filename = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '.phplint.yml';

return realpath($filename);
}

protected function loadConfiguration(string $path): array
{
try {
$configuration = Yaml::parse(file_get_contents($path));
if (!is_array($configuration)) {
throw new ParseException('Invalid content.', 1);
}

return $configuration;
} catch (ParseException $e) {
$this->output->writeln(sprintf('<error>Unable to parse the YAML string: %s</error>', $e->getMessage()));

return [];
}
}
}
Loading

0 comments on commit c633e8a

Please sign in to comment.