1

Symfony provides other classes that implement OutputInterface. How can I provide instances of these classes - ideally from the command line or other config options - to a command?

My current workaround for using different Output objects is to immediately reassign $output to the preferred object like so:

<?php

namespace AppBundle\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\NullOutput;

class DebugCommand extends Command
{
    protected function Configure()
    {
        $this->setName('AppBundle:DebugCommand');

    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output = new NullOutput();
        $output->writeln('Done!');
    }
}

But this feels sloppy. It would make much more sense to simply provide the intended object as a parameter to DebugCommand::execute(). Plus, If I decided I did want the output - I would have to modify the code to get the intended behavior.

How can I achieve this properly?

EDIT:

My hope is that it will be possible to set a default for each command. This would be helpful because I could create a new class that implements OutputInterface that would post output to, say, my team's Slack channel. But a different command might need to post to a different team's Slack channel. Being able to customize the output object for each command would be helpful as each command might affect different teams.

2
  • Are you using the framework or just the console component? Commented Mar 25, 2016 at 16:41
  • @Laoneo The whole framework - but if there's an answer that address just using the component, I'd be pretty interested to see that too. Commented Mar 25, 2016 at 16:41

1 Answer 1

1

When you are using the framework, then you do have similar code in your console script:

$application = new Application($kernel);
$application->setDefaultCommand('default');
$application->run($input);

what you can do now is to add a second argumen to the run function, like:

$application->run($input, new NullOutput());

EDIT:

How to do it per command needs a new class which extends the Application class:

class SlackOutputApplication extends Application{
    protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
    {
        if ($command->getName() == 'foo') {
            $output = new SlackOutput('channelname');
        }
        parent::doRunCommand($command, $input, $output);
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

This is very helpful, but doing this will affect all of my commands application wide - is it possible to fine tune the output for each command?
I guess then you need to overwrite the application class and overwrite the doRunCommand.
That's an interesting idea - but all it appears to be doing is pushing the bad practice up the call stack, which IMO is less practical than reassigning it within the command. This way, a new dev would have to know to look inside SlackOutputApplication to determine why they can't view the output on the command line. Plus, I'd probably opt to put Slack URLs/channel names/whatever in a parameter within the container. It doesn't look like Output instances are supposed to know of the container, where the command can (if extending ContainerAwareCommand). I might have to add a feature request
For sure I would inject something into the command constructor which knows already to which channel to write to. But I tried to answer your question with a simple example. What you make out of it is up to you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.