-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Console] Added support for lazy-loaded commands #13946
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Refactored the commands to extract the configuration into a separate class. That allows to define the configuration of a command without instantiating the command. The command is then loaded only when being used.
The build on 2.7 is currently broken which explains the failing PR, I will rebase when it is fixed. Locally tests are passing. |
Honestly, I don't understand why we can't (re)use existing Anyway, I definitely like to see separate |
Yes it's possible but I consider it a cheap workaround rather than a solution. With that you would have to write commands that do not extend the Instead of putting the command out of the
I don't I understand that part sorry? |
Why? Closure can resolve command and execute it: $command = new new Command(null, $configuration);
$command->setCode(function ($input, $output, $configuration) {
// Feel free to resolve command instance from DI container
$internal = new GreetCommand(null, $configuration);
return $internal->run($input, $output);
}); You can add this 3rd argument without breaking BC.
I'd like to see a value object, not a mutable one. |
@unkind Your example is a workaround. That's also the kind of workaround I have implemented for using in Piwik and we are not using it yet (and not released) because "it's not the Symfony API so people have to learn the Piwik console version (and we have to maintain it)". So yes of course you can always find a way, but here I'm suggesting to improve Symfony Console to have an easy and clean solution available for everyone instead of releasing a "fork" (or rather alternative to symfony/console). |
I don't state it isn't. The only thing I don't like in your one is that AFAIK, Symfony 2.7 is already closed for new features according to http://symfony.com/doc/current/contributing/community/releases.html. If it isn't, I can make alternative PR. However, I don't see much sense in these workarounds for 3.0. |
2.7 will not accept any new features at the end of the month, so we still have time. |
@unkind Ah you mean Yes I don't find this perfect too and I'd rather introduce a |
@mnapoli by the way, you should remove |
@mnapoli what is the status of this pull request ? |
@mickaelandrieu it is waiting for some love :) I.e. waiting for a core team member to tell me:
|
I would prefer a way staying closer to the current architecture, as I explained in #13989 (comment) as well |
@stof OK I can try a different approach, just to be clear the name and aliases would have to be defined outside the command right? Here is a solution using callbacks as a factory: $callable = function ($commandName) {
// return the command instance
};
$app->addLazyCommand('name', ['aliases'], $callable); Or we could introduce a interface CommandResolver {
public function getCommand($name);
}
$app->setCommandResolver($resolver);
$app->addLazyCommand('name', ['aliases'], 'command-id'); Thoughts before I go with an implementation? |
👍 |
Any update on this? I'm having major issues right now due to a The only way I can fix it is by fetching the soap service from the container at a later stage and this is something I'm hoping to avoid. /ping @mnapoli |
On my side I have nothing new to add here, I opened the PR and also offered alternative ideas for implementations so I'm waiting for feedback ;) And since I opened this PR I keep seeing this problem more and more, for example on Piwik as we introduce dependency injection more and more. |
@iltar For now, you could declare the SoapClient service lazy, can't you? |
@xabbuh it's an option but I actually wrote my own interfaced wrapper for it, which in turn is a nicer solution than it originally was by calling the SoapClient directly. I don't like introducing lazy services via that package as the generated proxies provide some obstacles. The php SoapClient is just bad by design. |
What state is this in? |
👍 |
Have a look also at #22734 which seems to be a very good solution (maybe better than this one because simpler). |
Closing since a better approach is being worked on. |
@nicolas-grekas Could you link that one please? |
@TomasVotruba I think he was referring to #22734 |
@theofidry I prefer links over guessing. Thanks. |
🎉 thanks @chalasr |
This PR was merged into the 3.4 branch. Discussion ---------- [Console] Add support for command lazy-loading | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #12063 #16438 #13946 #21781 | License | MIT | Doc PR | todo This PR adds command lazy-loading support to the console, based on PSR-11 and DI tags. (#12063 (comment)) Commands registered as services which set the `command` attribute on their `console.command` tag are now instantiated when calling `Application::get()` instead of all instantiated at `run()`. __Usage__ ```yaml app.command.heavy: tags: - { name: console.command, command: app:heavy } ``` This way private command services can be inlined (no public aliases, remain really private). With console+PSR11 implem only: ```php $application = new Application(); $container = new ServiceLocator(['heavy' => function () { return new Heavy(); }]); $application->setCommandLoader(new ContainerCommandLoader($container, ['app:heavy' => 'heavy']); ``` Implementation is widely inspired from Twig runtime loaders (without the definition/runtime separation which is not needed here). Commits ------- 7f97519 Add support for command lazy-loading
This PR was merged into the 3.4 branch. Discussion ---------- [Console] Add support for command lazy-loading | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | symfony/symfony#12063 symfony/symfony#16438 symfony/symfony#13946 #21781 | License | MIT | Doc PR | todo This PR adds command lazy-loading support to the console, based on PSR-11 and DI tags. (symfony/symfony#12063 (comment)) Commands registered as services which set the `command` attribute on their `console.command` tag are now instantiated when calling `Application::get()` instead of all instantiated at `run()`. __Usage__ ```yaml app.command.heavy: tags: - { name: console.command, command: app:heavy } ``` This way private command services can be inlined (no public aliases, remain really private). With console+PSR11 implem only: ```php $application = new Application(); $container = new ServiceLocator(['heavy' => function () { return new Heavy(); }]); $application->setCommandLoader(new ContainerCommandLoader($container, ['app:heavy' => 'heavy']); ``` Implementation is widely inspired from Twig runtime loaders (without the definition/runtime separation which is not needed here). Commits ------- 7f97519 Add support for command lazy-loading
This PR adds support for lazy-loaded commands in the Console component (#12063).
I had to refactor the commands to extract the configuration into a separate class. That allows to define the configuration of a command separately from the command itself, which means we don't have to instantiate it. The command is then loaded only when being used. This is particularly useful when writing an application with a dependency injection container.
Backward compatibility is kept (if not it's a bug to fix) as
Command
extend from the newCommandConfiguration
class (i.e. current commands are their own configuration). I wish they didn't as composition would be better instead of inheritance here, suggestions are welcome. Maybe we can break BC if we are targeting 3.0 and the change is deemed good enough?To define a lazy-loaded command, here is a simple example:
Lazy-loaded commands don't have to define
configure()
:I hesitated between using closures that return the Command instance, or introducing a
CommandResolver
. I went with the simpler solution.As explained in #12063, the current implementation forces to create all the Command instances which has the following problems for example:
The impact and reality of those problems depend a lot of your application obviously. But just like an application shouldn't load every controller and their dependencies on each request, the console application shouldn't load every command and their dependencies on each execution if that can be avoided.
I don't believe this change has any impact on the Symfony full-stack framework but maybe it will give you some ideas.