How to improve your Symfony commands
November 17, 2020
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
.
1composer create-project symfony/website-skeleton sandbox
2cd sandbox
3composer 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.
1<?php
2
3declare(strict_types=1);
4
5namespace App\Command;
6
7use Symfony\Component\Console\Application;
8use Symfony\Component\Console\Command\Command;
9use Symfony\Component\Console\Input\ArrayInput;
10use Symfony\Component\Console\Input\InputInterface;
11use Symfony\Component\Console\Output\OutputInterface;
12
13class DefaultCommand extends Command
14{
15 /**
16 * {@inheritdoc}
17 */
18 protected static $defaultName = 'app:default';
19
20 /**
21 * {@inheritdoc}
22 */
23 protected function configure(): void
24 {
25 $this->setDescription('Wrapper of the default "list" command');
26 $this->setHidden(true);
27 }
28
29 # ...
30}
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.
1 /**
2 * {@inheritdoc}
3 */
4 protected function execute(InputInterface $input, OutputInterface $output): int
5 {
6 /** @var Application $application */
7 $application = $this->getApplication();
8
9 $command = $application->find('list');
10 $arguments = ['namespace' => 'app'];
11
12 $listInput = new ArrayInput($arguments);
13
14 return $command->run($listInput, $output);
15 }
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.
1$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
2$application = new Application($kernel);
3+ $application->setDefaultCommand(\App\Command\DefaultCommand::getDefaultName());
4$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.
1- use Symfony\Bundle\FrameworkBundle\Console\Application;
2+ use App\Application;
1<?php
2
3declare(strict_types=1);
4
5namespace App;
6
7class Application extends \Symfony\Component\Console\Application
8{
9 /**
10 * {@inheritdoc}
11 *
12 * @param Kernel $kernel
13 */
14 public function __construct(KernelInterface $kernel)
15 {
16 parent::__construct($kernel);
17
18 if ($defaultName = DefaultCommand::getDefaultName()) {
19 $this->setDefaultCommand($defaultName);
20 }
21 }
22}
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.
1<?php
2
3declare(strict_types=1);
4
5namespace App;
6
7use App\Command\DefaultCommand;
8use Symfony\Bundle\FrameworkBundle\Console\Application as SymfonyApplication;
9use Symfony\Component\HttpKernel\KernelInterface;
10
11class Application extends SymfonyApplication
12{
13 public const CONSOLE_LOGO = <<<'ASCII'
14 ___ _ _ _
15 / __| ___ _ __ ___| |_| |_ (_)_ _ __ _
16 \__ \/ _ \ ' \/ -_) _| ' \| | ' \/ _` |
17 |___/\___/_|_|_\___|\__|_||_|_|_||_\__, |
18 |___/
19
20
21ASCII;
22
23 public const CONSOLE_NAME = 'Something';
24
25 /**
26 * {@inheritdoc}
27 *
28 * @param Kernel $kernel
29 */
30 public function __construct(KernelInterface $kernel)
31 {
32 parent::__construct($kernel);
33
34 if ($defaultName = DefaultCommand::getDefaultName()) {
35 $this->setDefaultCommand($defaultName);
36 }
37 }
38
39 /**
40 * {@inheritdoc}
41 */
42 public function getHelp(): string
43 {
44 return self::CONSOLE_LOGO.parent::getHelp();
45 }
46
47 /**
48 * {@inheritdoc}
49 */
50 public function getName(): string
51 {
52 return self::CONSOLE_NAME;
53 }
54
55 /**
56 * {@inheritdoc}
57 */
58 public function getVersion(): string
59 {
60 return 'X.Y.Z';
61 }
62}
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.