Search code examples
phpsymfonycommand-line-interfacesymfony-console

Symfony Console - Overwrite default options


I'm building a CLI application for my app using Symfony Console. The application is required to do some simple package management operations to manage plugins. Therefore, I need a command option named --version with shortcut -v, which does not seem to be possible since --version is reserved for the application version and -v is reserved for the default verbosity setting.
How can I either disable the default options or overwrite them for this subcommand?

The error popping up states

[Symfony\Component\Console\Exception\LogicException] An option named "version" already exists.


Solution

  • So, here is a solution that works, but I don't recommend it. I recommend you simply use --package-version or some other sort of option for your command, because this requires a lot of extra work and doesn't play well for future Symfony updates.

    The first thing you can do is copy the console command in your bin/ directory to a new command, and change the use statement to your own extended Application:

    bin/alt_console:

    // use Symfony\Bundle\FrameworkBundle\Console\Application;
    use AppBundle\Console\Application;
    

    Then create a new Application class that extends the existing Symfony Application, that looks like this:

    Application:

    namespace AppBundle\Console;
    
    use AppBundle\Command\YourCommand;
    use Symfony\Component\Console\Application;
    use Symfony\Component\Console\Event\ConsoleErrorEvent;
    use Symfony\Component\Console\Input\ArrayInput;
    use Symfony\Component\Console\Input\InputDefinition;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    
    class TestApplication extends Application
    {
        const NAME = 'Alternative Application';
        const VERSION = '1.0';
    
        // these are normally private, but must now be protected as they exist in doRun()
        protected $command;
        protected $defaultCommand;
        protected $dispatcher;
        protected $runningCommand;
    
        /**
         * {@inheritdoc}
         */
        public function __construct()
        {
            parent::__construct(static::NAME, static::VERSION);
    
            // manually add the commands you want to be handled by this
            $this->add(new YourCommand());
        }
    
        /**
         * {@inheritdoc}
         */
        public function doRun(InputInterface $input, OutputInterface $output)
        {
            /* remove these lines
            if (true === $input->hasParameterOption(array('--version', '-V'), true)) {
                $output->writeln($this->getLongVersion());
    
                return 0;
            }
            */
    
            // copy the rest of the doRun() function as it 
            // exists in the base Application class
            // ...
        }
    
        /**
         * {@inheritdoc}
         *
         * Return everything from the default input definition except the 'version' option
         */
        protected function getDefaultInputDefinition()
        {
            $definition  = [];
    
            $defaultDefinition = parent::getDefaultInputDefinition();
    
            foreach ($defaultDefinition->getOptions() as $option) {
                if ($option->getName() !== 'version') {
                    $definition[] = $option;
                }
            }
    
            foreach ($defaultDefinition->getArguments() as $argument) {
                $definition[] = $argument;
            }
    
            return new InputDefinition($definition);
        }
    }
    

    Now add your application with your own --version option:

    namespace AppBundle\Command;
    
    use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Input\InputOption;
    use Symfony\Component\Console\Output\OutputInterface;
    
    class YourCommand extends ContainerAwareCommand
    {
        /**
         * {@inheritdoc}
         */
        protected function configure()
        {
            $this
                ->setName('your:command')
                ->addOption('--version', '-V', InputOption::VALUE_NONE, 'okay')
            ;
        }
    
        /**
         * {@inheritdoc}
         */
        protected function execute(InputInterface $input, OutputInterface $output)
        {
            if ($input->hasOption('version')) {
                // do something
            }
        }
    }
    

    Then you can call your command via:

    php bin/console your:command
    php bin/console your:comand --version
    

    Note that I do NOT recommend this. This is a lot of extra work for almost no gain at all other than saving yourself a few keystrokes with a different option now. Plus if bin/console or Symfony\Component\Console\Application changes in the future, or you'll have to manually update those files since you overrode them.