From 8ea28ba3fbd5de2b8ed3c21214dc8a26e29df79e Mon Sep 17 00:00:00 2001 From: Gert de Pagter Date: Thu, 1 Oct 2020 23:16:52 +0200 Subject: [PATCH] Allow to lint for warnings --- .phplint.yml | 1 + README.md | 13 +++++++++- src/Command/LintCommand.php | 32 ++++++++++++++++++++--- src/Linter.php | 12 ++++++++- src/Process/Lint.php | 52 +++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 5 deletions(-) diff --git a/.phplint.yml b/.phplint.yml index 256dd19b..f51c3c4c 100644 --- a/.phplint.yml +++ b/.phplint.yml @@ -4,3 +4,4 @@ extensions: - php exclude: - vendor +warning: true diff --git a/README.md b/README.md index 94922bba..04588dda 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Options: --cache=CACHE Path to the cache file. --json[=JSON] Output JSON results to a file. --xml[=XML] Output JUnit XML results to a file. + -w, --warning Also show warnings -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version @@ -72,6 +73,7 @@ extensions: - php exclude: - vendor +warning: false ``` ```shell @@ -82,6 +84,14 @@ By default, the command will read configuration from file `.phplint.yml` of path If you want to disable the config file, you can add option `--no-configuration`. +### Warnings + +Not all linting problems are errors, PHP also has warnings, for example when using a `continue` statement within a +`switch` `case`. By default these errors are not reported, but you can turn this on with the `warning` cli flag, or +by setting the `warning` to true in the configuration. + + + ### Program ```php @@ -90,8 +100,9 @@ use Overtrue\PHPLint\Linter; $path = __DIR__ .'/app'; $exclude = ['vendor']; $extensions = ['php']; +$warnings = true; -$linter = new Linter($path, $exclude, $extensions); +$linter = new Linter($path, $exclude, $extensions, $warnings); // get errors $errors = $linter->lint(); diff --git a/src/Command/LintCommand.php b/src/Command/LintCommand.php index be9262b4..0aa52531 100644 --- a/src/Command/LintCommand.php +++ b/src/Command/LintCommand.php @@ -42,6 +42,7 @@ class LintCommand extends Command 'path' => '.', 'exclude' => [], 'extensions' => ['php'], + 'warning' => false ]; /** @@ -126,6 +127,12 @@ protected function configure() null, InputOption::VALUE_OPTIONAL, 'Path to store JUnit XML results.' + ) + ->addOption( + 'warning', + 'w', + InputOption::VALUE_NONE, + 'Also show warnings' ); } @@ -177,7 +184,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln('Options: ' . json_encode($options) . "\n"); } - $linter = new Linter($options['path'], $options['exclude'], $options['extensions']); + $linter = new Linter($options['path'], $options['exclude'], $options['extensions'], $options['warning']); $linter->setProcessLimit($options['jobs']); if (!empty($options['cache'])) { @@ -304,13 +311,29 @@ protected function executeLint($linter, $input, $output, $fileCount) if ($verbosity >= OutputInterface::VERBOSITY_VERBOSE) { $filename = str_pad(" {$i}: " . $file->getRelativePathname(), $maxColumns - 10, ' ', \STR_PAD_RIGHT); - $status = \str_pad(('ok' === $status ? 'OK' : 'Error'), 20, ' ', \STR_PAD_RIGHT); + if ($status === 'ok') { + $status = 'OK'; + } elseif ($status === 'error') { + $status = 'Error'; + } else { + $status = 'Warning'; + } + + $status = \str_pad($status, 20, ' ', \STR_PAD_RIGHT); $output->writeln(\sprintf("%s\t%s\t%s", $filename, $status, $process)); } else { if ($i && 0 === $i % $maxColumns) { $output->writeln($process); } - $output->write('ok' === $status ? '.' : 'E'); + if ($status === 'ok') { + $status = '.'; + } elseif ($status === 'error') { + $status = 'E'; + } else { + $status = 'W'; + } + + $output->write($status); } ++$i; }); @@ -404,6 +427,9 @@ protected function mergeOptions() $options = $this->input->getOptions(); $options['path'] = $this->input->getArgument('path'); $options['cache'] = $this->input->getOption('cache'); + if ($options['warning'] === false) { + unset($options['warning']); + } $config = []; diff --git a/src/Linter.php b/src/Linter.php index 34151854..86bda986 100644 --- a/src/Linter.php +++ b/src/Linter.php @@ -56,20 +56,27 @@ class Linter */ private $processLimit = 5; + /** + * @var bool + */ + private $warning; + /** * Constructor. * * @param string|array $path * @param array $excludes * @param array $extensions + * @param bool $warning */ - public function __construct($path, array $excludes = [], array $extensions = ['php']) + public function __construct($path, array $excludes = [], array $extensions = ['php'], $warning = false) { $this->path = (array)$path; $this->excludes = $excludes; $this->extensions = \array_map(function ($extension) { return \sprintf('*.%s', \ltrim($extension, '.')); }, $extensions); + $this->warning = $warning; } /** @@ -124,6 +131,9 @@ public function lint($files = [], $cache = true) if ($lint->hasSyntaxError()) { $processCallback('error', $item['file']); $errors[$filename] = array_merge(['file' => $filename, 'file_name' => $item['relativePath']], $lint->getSyntaxError()); + } elseif ($this->warning && $lint->hasSyntaxWarning()) { + $processCallback('warning', $item['file']); + $errors[$filename] = array_merge(['file' => $filename, 'file_name' => $item['relativePath']], $lint->getSyntaxWarning()); } else { $newCache[$item['relativePath']] = md5_file($filename); $processCallback('ok', $item['file']); diff --git a/src/Process/Lint.php b/src/Process/Lint.php index 89060b01..9792c55d 100644 --- a/src/Process/Lint.php +++ b/src/Process/Lint.php @@ -68,4 +68,56 @@ public function parseError($message) 'line' => $matched ? abs($match['line']) : 0, ]; } + + /** + * @return bool + */ + public function hasSyntaxWarning() + { + $output = trim($this->getOutput()); + + if (defined('HHVM_VERSION') && empty($output)) { + return false; + } + + + return false !== strpos($output, 'Warning: '); + } + + /** + * @return bool|array + */ + public function getSyntaxWarning() + { + if ($this->hasSyntaxWarning()) { + $out = explode("\n", trim($this->getOutput())); + + return $this->parseWarning(array_shift($out)); + } + + return false; + } + + /** + * Parse error message. + * + * @param string $message + * + * @return array + */ + public function parseWarning($message) + { + $pattern = '/^(PHP\s+)?Warning:\s*\s*?(?.+?)\s+in\s+.+?\s*line\s+(?\d+)/'; + + $matched = preg_match($pattern, $message, $match); + + if (empty($message)) { + $message = 'Unknown'; + } + + return [ + 'error' => $matched ? "{$match['error']} in line {$match['line']}" : $message, + 'line' => $matched ? abs($match['line']) : 0, + ]; + } }