How to improve your Symfony commands
When we develop a Symfony project, we inherit a lot of default commands. For example, if we create a new project
starting from symfony/website-skeleton
, we already have more than 80 commands. We can prefix all our custom commands
with app:
so that they don’t get mixed up, but the display remains somewhat overloaded.
I’m going to present some of the tricks I configure on most of my Symfony projects.
The main one consists of creating a new default command that will allow us to filter what is displayed via the
bin/console
instruction so that there are only our custom commands. Don’t worry! The bin/console list
behaviour
will not change to let us see all available commands.
Initialization
To illustrate this post, we will generate a new Symfony project using website-skeleton
.
composer create-project symfony/website-skeleton sandbox
cd sandbox
composer install --optimize-autoloader
Default command
Let’s start by creating our new default command: src/Command/DefaultCommand.php
. In addition to the usual elements, we
explicitly state that this is a hidden command that will not appear in the lists.
<?php
declare(strict_types=1);
namespace App\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DefaultCommand extends Command
{
/**
* {@inheritdoc}
*/
protected static $defaultName = 'app:default';
/**
* {@inheritdoc}
*/
protected function configure(): void
{
$this->setDescription('Wrapper of the default "list" command');
$this->setHidden(true);
}
# ...
}
The execute
method will probably be a bit different from the one you are used to writing since we will use an already
existing command: list
. Therefore, we are also going to add an extra parameter to it, the one that allows us to
display only our custom commands.
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var Application $application */
$application = $this->getApplication();
$command = $application->find('list');
$arguments = ['namespace' => 'app'];
$listInput = new ArrayInput($arguments);
return $command->run($listInput, $output);
}
Custom application
To invoke this new command when you write bin/console
, you will need to configure the application default command.
There are two ways to achieve this.
You can edit the bin/console
file to add the default command statement.
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = new Application($kernel);
+ $application->setDefaultCommand(\App\Command\DefaultCommand::getDefaultName());
$application->run($input);
You can also create your own Application
class and instantiate it instead of the Symfony class, still within the
bin/console
file.
- use Symfony\Bundle\FrameworkBundle\Console\Application;
+ use App\Application;
<?php
declare(strict_types=1);
namespace App;
class Application extends \Symfony\Component\Console\Application
{
/**
* {@inheritdoc}
*
* @param Kernel $kernel
*/
public function __construct(KernelInterface $kernel)
{
parent::__construct($kernel);
if ($defaultName = DefaultCommand::getDefaultName()) {
$this->setDefaultCommand($defaultName);
}
}
}
I prefer this second option. It may be more verbose, but it allows for more customizations in addition to the default command.
- Adding a custom header before the commands.
- The configuration of a custom name to replace
Symfony
. - The configuration of a custom version to replace the Symfony version.
Here is a complete example with all these customizations.
<?php
declare(strict_types=1);
namespace App;
use App\Command\DefaultCommand;
use Symfony\Bundle\FrameworkBundle\Console\Application as SymfonyApplication;
use Symfony\Component\HttpKernel\KernelInterface;
class Application extends SymfonyApplication
{
public const CONSOLE_LOGO = <<<'ASCII'
___ _ _ _
/ __| ___ _ __ ___| |_| |_ (_)_ _ __ _
\__ \/ _ \ ' \/ -_) _| ' \| | ' \/ _` |
|___/\___/_|_|_\___|\__|_||_|_|_||_\__, |
|___/
ASCII;
public const CONSOLE_NAME = 'Something';
/**
* {@inheritdoc}
*
* @param Kernel $kernel
*/
public function __construct(KernelInterface $kernel)
{
parent::__construct($kernel);
if ($defaultName = DefaultCommand::getDefaultName()) {
$this->setDefaultCommand($defaultName);
}
}
/**
* {@inheritdoc}
*/
public function getHelp(): string
{
return self::CONSOLE_LOGO.parent::getHelp();
}
/**
* {@inheritdoc}
*/
public function getName(): string
{
return self::CONSOLE_NAME;
}
/**
* {@inheritdoc}
*/
public function getVersion(): string
{
return 'X.Y.Z';
}
}
Conclusion
It is not for nothing that Symfony Console is the most downloaded Symfony component. It is both powerful and straightforward to configure. I hope that this article will have taught you at least a little bit about it.
Thanks for reading!
This post is also published on DEV.
Feel free to go there if you wish to react or participate in the discussion.