New in Symfony 3.4: Lazy commands

Contributed by
Robin Chalas
in #22734.

The Symfony Console component is the second most popular Symfony component (more than 54 million downloads) and is used by the most important PHP projects, including Drupal and Magento.

Despite its great features, the Symfony Console suffered a shortcoming since day one: you must instantiate all commands to register them in the console application. The reason is that even the command name itself is defined inside a method called configure(), so you must instantiate the command class to configure it and get the command name.

In practice, this shortcoming is not a real problem for lots of applications. However, if you have lots of commands or they are very complex (because they make use of a lot of services) this can hurt performance. Moreover, if there is a single error when instantiating any of the commands, you can't run the application console, even if you are not executing the failing command.

There have been a lot of attempts to fix this problem (see #12063, #13946, #16438, #21781, etc.) In Symfony 3.4, we were finally able to fix this issue and console commands can now be lazy. This means that commands no longer need to be instantiated when running the console application, so executing any command will be a bit faster in Symfony 3.4.

In order to create a lazy-loaded command, first you must define the command as a service and then, you must add a command property to the console.command tag defining the name of the command (you can optionally add an alias attribute too):

app.command.complex_command:
    # ...
    tags:
        # the value of the 'command' attribute is the name of the command
        # (which is what the user needs to type in to execute it)
        - { name: console.command, command: app:my-command }

        # optionally you can define an alias for the command too
        - { name: console.command, command: app:my-command, alias: 'my-shortcut' }

And that's it! Thanks to our PSR-11 compatible containers and the dependency injection tags, this command will now be lazy loaded.

Comments

great! thanks!
Coooooool ! Finally !
Amazing work, I was soo waiting for this feature.
Just wondering, how does this work combined with "autoconfigure"? Can I only add "command: name" part or do I need to add tag as well?
So simple and elegant :-).
@TomášVotruba I think that autoconfiguration won't work in this case. However if we wanted to use class names then that would be simple to autoconfigure.
@Tomáš @Jáchym autoconfigure still works, but it won't make your command lazy as we can't guess the command name from it, you have to define the tag explicitly for using this.
@Robin Chalas I meant combination of autoconfiguration + name in a tag.

So from this:

services:
SomeCommand:
tags:
- { name: console.command, command: app:my-command }

To this:

services:
autoconfigure: true

SomeCommand:
tags:
- { command: app:my-command }
Does anyone know how to highlight code in here? :D
@Tomáš you can only use plain text in commands (no Markdown, no highlighting, nothing). I'm sorry!
Way cool! Will all the "normal" Symfony commands be refactored to use this new format?
@Tomáš The autoconfiguration in this case would most likely have this result:

SomeCommand:
tags:
- { command: app:my-command }
- { name: console.command }

Which would cause an exception 'A "tags" entry is missing a "name" key for service "SomeCommand"'.
@Tomáš Votruba @Jáchym Toušek

It was broken! The command will be lazy now, the explicit tag wins over autoconfigure.
See https://github.com/symfony/symfony/pull/23556

@Tarjei Huse

most of core commands should be updated
I'm playing with this and "list" seems broken now, it doesn't report any command other than "help" and "list". Is that so?
@Olivier Fixed in https://github.com/symfony/symfony/pull/23556
Useful !
Great! Thanks!

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.