phpsymfonysymfony-console

Get the whole console output from Symfony 5 OutputInterface


I've a Symfony 5 command like this:

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

....

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->input        = $input;
        $this->output       = $output;
        $this->io           = new SymfonyStyle($input, $output);
        ....
    }

This command generates A LOT of output with $this->io->caution(...), $this->io->block(....), $this->io->text(....) and so on.

Sometimes (not always: there are some runtime conditions), at the end of the execution, I'd like to access the whole output generated by the command and then send it via email. So.... how can I get back everything that OutputInterface has shown? Is there some kind of $this->output->getBuffer()?

I'd have no problem swapping OutputInterface $output with something else (logger, maybe?) as long as I can still show everything on the stdoutput (my terminal) as I'm doing at the moment.


Solution

  • I don't think there is anything off-the-shelf that accomplish this for you. You may achieve something like this with loggers... but you would have to fiddle a lot with configuration error levels, probably inject more than one logger, the console output would never match the one by SymfonyStyle, etc.

    You are better served building your own, which shouldn't be particularly difficult. Just build something that wraps/decorates SymfonyStyle; and captures output.

    I'll provide the starting building blocks, it's up to you to finish the implementation:

    class LoggingStyle
    {
        private SymfonyStyle $style;
        private array        $logEntries = [];
    
        public function __construct(InputInterface $input, OutputInterface $output) {
            $this->style = new SymfonyStyle($input, $output);
        }
    
        private function addLog(string $type, string $message): void
        {
            $this->logEntries[] = [
                'type' => $type,
                'message' => $message,
                'time' => date('Y-m-d H:i:s')
            ];
        }
    
        public function flushLog():array
        {
            $log = $this->logEntries;
            $this->logEntries = [];
    
            return $log;
        }
    
        public function caution(string $message): void
        {
            $this->addLog('caution', $message);
            $this->style->caution($message);
        }
    
        public function text(string $message): void
        {
            $this->addLog('text', $message);
            $this->style->caution($message);
        }
    
        public function block(string $message): void
        {
            $this->addLog('block', $message);
            $this->style->caution($message);
        }
    }
    
    

    You'd need to implement every part of SymfonyStyle interface you need to use, and decide how to deal with some special behaviour, if at all (e.g. stuff like ask(), table(), or progress bars). But that would be entirely up to your implementation.

    And also decide how to format each of the different output styles in your email, since there is logically no way to translate it directly.

    You could use this class directly, and if you reach the point where you need the aggregated output you simply call LoggingStyle::flushLog() and get all the entries in array form, for you to process and send accordingly.