A forgiving command loader for Symfony Console

Chris Harrison
Sep 26, 2018 · 2 min read

This article summaries a solution to the problem of calling and listing commands in Symfony Console when at least one of the commands is not instantiable.

The problem

By default, Symfony will instantiate all your application’s commands when initialising. If just one of the commands fails to initialize, the entire application will fail — even when executing a command that works fine.

Ordinarily, this shouldn’t be a problem. After all, classes should be instantiable, shouldn’t they? But, in my work at Funeral Zone, we have a use-case of a console application that sits in multiple microservices. Some of the commands in the application are reliant on environment variables (such as database credentials) that are not necessary for all microservices. This means, on a microservice that doesn’t require a database, the database related commands can’t initialize — and they bring down the rest of the console application with them!

Command loading

The solution is what Symfony calls ‘lazy loading’. This is simply retrieving the commands from the IoC container using a ‘command loader’. Symfony Console conveniently provides us with a CommandLoaderInterface and a ContainerCommandLoader implementation.

$consoleApp = new ConsoleApplication;
$consoleApp->setCommandLoader(
new ContainerCommandLoader($iocContainer, [
'command:one' => CommandOne::class,
'command:two' => ComamndTwo::class,
])
));

When the console application executes a command…

php console.php command:one

The CommandOne command will be retrieved from the IoC container. If it fails, this does not stop CommandTwo from being loaded — because the commands are only loaded when executed.

Remaining problem of listing

So far, we’ve solved the problem of non-instantiable commands blocking all other commands. However, listing (php console.php list) all of the console application’s commands is still not possible if just one of the commands is not instantiable.

This Symfony documentation confirms that:

Calling the list command will instantiate all commands, including lazy commands.

The solution to this is what I’ve termed a ‘forgiving command loader’. I’ve published it on GitHub. It requires another CommandLoaderInterface to be injected into it. Then, using the composition principle, it proxies the internal loader. If it encounters an exception when loading a command, it returns a placeholder command. It sets the description of the placeholder command to ‘** Could not load’. This way, you can still list all the commands in your application.

php console.php listAvailable commands:
command:one Our first command
command:two ** Could not load
command:three Our third command
command:four Our fourth command

If you try to execute command:two you will receive the exception that was originally caught when loading.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store