* Dariusz RumiƄski * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace PhpCsFixer\Console\Output; use PhpCsFixer\Differ\DiffConsoleFormatter; use PhpCsFixer\Error\Error; use PhpCsFixer\Linter\LintingException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Output\OutputInterface; /** * @internal */ final class ErrorOutput { private OutputInterface $output; /** * @var bool */ private $isDecorated; public function __construct(OutputInterface $output) { $this->output = $output; $this->isDecorated = $output->isDecorated(); } /** * @param Error[] $errors */ public function listErrors(string $process, array $errors): void { $this->output->writeln(['', sprintf( 'Files that were not fixed due to errors reported during %s:', $process )]); $showDetails = $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE; $showTrace = $this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG; foreach ($errors as $i => $error) { $this->output->writeln(sprintf('%4d) %s', $i + 1, $error->getFilePath())); $e = $error->getSource(); if (!$showDetails || null === $e) { continue; } $class = sprintf('[%s]', \get_class($e)); $message = $e->getMessage(); $code = $e->getCode(); if (0 !== $code) { $message .= " ({$code})"; } $length = max(\strlen($class), \strlen($message)); $lines = [ '', $class, $message, '', ]; $this->output->writeln(''); foreach ($lines as $line) { if (\strlen($line) < $length) { $line .= str_repeat(' ', $length - \strlen($line)); } $this->output->writeln(sprintf(' %s ', $this->prepareOutput($line))); } if ($showTrace && !$e instanceof LintingException) { // stack trace of lint exception is of no interest $this->output->writeln(''); $stackTrace = $e->getTrace(); foreach ($stackTrace as $trace) { if (isset($trace['class']) && Command::class === $trace['class'] && 'run' === $trace['function']) { $this->output->writeln(' [ ... ]'); break; } $this->outputTrace($trace); } } if (Error::TYPE_LINT === $error->getType() && 0 < \count($error->getAppliedFixers())) { $this->output->writeln(''); $this->output->writeln(sprintf(' Applied fixers: %s', implode(', ', $error->getAppliedFixers()))); $diff = $error->getDiff(); if (null !== $diff) { $diffFormatter = new DiffConsoleFormatter( $this->isDecorated, sprintf( ' ---------- begin diff ----------%s%%s%s ----------- end diff -----------', PHP_EOL, PHP_EOL ) ); $this->output->writeln($diffFormatter->format($diff)); } } } } /** * @param array{ * function?: string, * line?: int, * file?: string, * class?: class-string, * type?: '::'|'->', * args?: mixed[], * object?: object, * } $trace */ private function outputTrace(array $trace): void { if (isset($trace['class'], $trace['type'], $trace['function'])) { $this->output->writeln(sprintf( ' %s%s%s()', $this->prepareOutput($trace['class']), $this->prepareOutput($trace['type']), $this->prepareOutput($trace['function']) )); } elseif (isset($trace['function'])) { $this->output->writeln(sprintf(' %s()', $this->prepareOutput($trace['function']))); } if (isset($trace['file'])) { $this->output->writeln(sprintf(' in %s at line %d', $this->prepareOutput($trace['file']), $trace['line'])); } } private function prepareOutput(string $string): string { return $this->isDecorated ? OutputFormatter::escape($string) : $string; } }