Add a required argument to every symfony/console command

icanhazstring

Andreas Frömer

Posted on June 3, 2020

Add a required argument to every symfony/console command

I would assume that everyone has fiddled around with symfony/console right? If not, you should probably check it out here. It is pretty easy to use and understand.

One thing I had problems today is: How do you add a required argument for every command that is added to the console application?

I came up with 3 solutions:

  1. Adding a BaseCommand class which every command added has to extend from
  2. Add a required argument to the Console\Application
  3. Add a required argument for each Console\Command added to the Console\Application

TLDR;

Like President Schwarzenegger used to say in the Simpson Movie: "I pick number three".
This way for me is most robust change as you don't need to alter any command and every new command that will be added, will receive the required argument.

Just as a side note: I used to pick the first approach :)

Adding a BaseCommand class which every command added has to extend from

This is pretty straight forward. You create a new BaseCommand class and add your required argument into the configure() method.

<?php

use Symfony\Component\Console\Command\Command;

class BaseCommand extends Command 
{
    public function configure(): void
    {
        $this->addArgument(
            'name',
            InputArgument::REQUIRED,
            'description'
       );
    }
}
Enter fullscreen mode Exit fullscreen mode

But, what this would require is you have to call parent::configure() in every command so that the argument is there, if not. Well its not.

Add a required argument to the Console\Application

This was the second approach I found while searching for a solution.
Sadly, every solution I found was adding a new optional argument to the definition, which is not what I needed, you might need to "fiddle" around with the setArguments() of the InputDefinition of an application.

You can do this inside you bootstrap file, whery you load the application.

$app = new Application();
$appDefinition = $app->getDefinition();

$appDefininition->setArguments(
    array_merge([$requiredArgument], $appDefinition->getArguments())
);
Enter fullscreen mode Exit fullscreen mode

But this is kind of out of scope where you want to define it.
So you can create your custom application class and do it there:

<?php

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputDefinition;

class CustomApplication extends Application
{
    protected function getDefaultInputDefinition(): InputDefinition
    {
        $definition = parent::getDefaultInputDefinition();

        $definition->setArguments(
            array_merge([$requiredArgument], $definition->getArguments())
        );

        return $definition;
    }
}
Enter fullscreen mode Exit fullscreen mode

Downside on this: You have a require argument for every command of your application even with the HelpCommand or ListCommand. So you can't run you application by simply running bin/console (if that is you entrypoint). You would need to pass the required argument everytime.

Add a required argument for each Console\Command added to the Console\Application

The solution I choose, was to add this argument for each command added to the application.

Some of you might think: Wait a second, what abount HelpCommand and ListCommand you mentioned before?

Yes you are right, every command, except commands from symfony/console namespace :)

So this is my solution: Creating my CustomApplication and overwriting the add(Command $command) adding my required argument to every added command to my application.

<?php

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;

class ConsoleApplication extends Application
{
    public function add(Command $command)
    {
        if (strpos(get_class($command), 'Symfony\Component\Console') === 0) {
            return parent::add($command);
        }

        $commandDefinition = $command->getDefinition();
        $commandDefinition->setArguments(
            array_merge([$requiredArgument], $commandDefinition->getArguments())
        );

        return parent::add($command);
    }
}
Enter fullscreen mode Exit fullscreen mode

There you have it. Every command will get your required argument. Also this will be the first argument in every command. So even if you have an optional argument configured, there won't be a problem with that.


Hope that will help some of you solving the same problem I had :)
If you have another clever solution, let me know!

💖 💪 🙅 🚩
icanhazstring
Andreas Frömer

Posted on June 3, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related