phplint
                                
                                 phplint copied to clipboard
                                
                                    phplint copied to clipboard
                            
                            
                            
                        [v9.4] Add support to more output formats
Summary
Simplify ability to add support to more formats
Context
Even if I consider SARIF as the futur of reporter solution, I must admit that PHP project leaders/maintainers are a little reluctant to set up this format, and prefer to implement checkstyle, junit, codeclimate ... to reference only few of them.
This is the reason why I've already provided a PHP binding solution https://github.com/llaville/sarif-php-sdk,, and I'm currently working on an improvement with upcoming version 2.0
My goal is to maintain all classes for PHP Linters and Static Code Analyser on a new package bartlett/sarif-php-converters and removed converters from base package package bartlett/sarif-php-sdk
See Reference below
Description
Even, If I've already implemented a solution into PHPLint, I must admit that it's a bit hard, and current OutputFormat extension did not allow to add support to more format easily. This is the MAIN goal of this feature request !
Recently, I've look on PHP Insights source code (especially the FormatResolver component), and I like ability to load custom format (not predefined).
BTW, it suffer from a problem that PHPLint has not : the bootstrapping option.
This is the main reason of new upcoming version 9.4.0 (/cc @overtrue)
Secondary goal is to simplify OutputFormat extension and respect the O (SRP) of SOLID principle.
And last but not least, clean-up SARIF current implementation in PHPLint.
v9.4.0 will come after I've finished https://github.com/llaville/sarif-php-converters and release the first version 1.0 with sarif-php-sdk 2.0
FormatResolver source code
<?php
declare(strict_types=1);
namespace Overtrue\PHPLint\Output;
use Overtrue\PHPLint\Configuration\OptionDefinition;
use Overtrue\PHPLint\Configuration\Resolver;
use Symfony\Component\Console\Input\InputInterface as SymfonyInputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface as SymfonyConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface as SymfonyOutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
use function array_key_exists;
use function array_merge;
use function class_exists;
use function fopen;
final class FormatResolver
{
    private const FORMATTERS = [
        'console' => ConsoleOutput::class,
        'json' => JsonOutput::class,
        'junit' => JunitOutput::class,
        'sarif' => SarifOutput::class,
    ];
    public function __construct(private readonly Resolver $configResolver)
    {
    }
    /**
     * @return OutputInterface[]
     */
    public function resolve(SymfonyInputInterface $input, SymfonyOutputInterface $output): array
    {
        $filename = $input->getOption('output');
        if ($filename) {
            $stream = fopen($filename, 'w');
        } else {
            $errOutput = $output instanceof SymfonyConsoleOutputInterface ? $output->getErrorOutput() : $output;
            if ($errOutput instanceof StreamOutput) {
                $stream = $errOutput->getStream();
            } else {
                $stream = fopen('php://stdout', 'w');
            }
        }
        $requestedFormats = array_merge($input->getOption('format'), $this->legacyLogOption());
        $handlers = [$output];
        foreach ($requestedFormats as $requestedFormat) {
            if ('console' === $requestedFormat) {
                // default behaviour
                continue;
            }
            if (array_key_exists($requestedFormat, self::FORMATTERS)) {
                // use built-in formatter
                $formatterClass = self::FORMATTERS[$requestedFormat];
                $handlers[] = new $formatterClass($stream, $output->getVerbosity(), $output->isDecorated(), $output->getFormatter());
                continue;
            }
            if (class_exists($requestedFormat)) {
                // try to load custom/external formatter
                $formatter = new $requestedFormat($stream, $output->getVerbosity(), $output->isDecorated(), $output->getFormatter());
                if (!$formatter instanceof OutputInterface) {
                    // skip invalid instance that does not implement contract
                    continue;
                }
                $handlers[] = $formatter;
            }
        }
        return $handlers;
    }
    /**
     * Checks if there is any `--log-[*]` legacy options
     *
     * @return string[]
     */
    private function legacyLogOption(): array
    {
        $outputOptions = [
            OptionDefinition::LOG_JSON => 'json',
            OptionDefinition::LOG_JUNIT => 'junit',
            OptionDefinition::LOG_SARIF => 'sarif',
        ];
        $requestedFormats = [];
        foreach ($outputOptions as $name => $format) {
            if ($this->configResolver->getOption($name)) {
                $requestedFormats[] = $format;
            }
        }
        return $requestedFormats;
    }
}
We will continue to support legacy options --log-[*] but we also add more generic
  -o, --output=OUTPUT                      Generate an output to the specified path (default: standard output)
      --format=FORMAT                      Format of requested reports (multiple values allowed)
Reference
Here are officially what I will support
| Converter | 
|---|
| Easy-Coding-Standard official website | 
| Phan official website | 
| PHP_CodeSniffer official website | 
| PHP-CS-Fixer official website | 
| PHPInsights official website | 
| PHPLint official website | 
| PHP Mess Detector official website | 
| PHP Magic Number Detector official website | 
| PHPStan official website |