Skip to content

Commit 98a0d7e

Browse files
committed
feature #8502 [Console] Command lazy loading (chalasr, javiereguiluz)
This PR was merged into the 3.4 branch. Discussion ---------- [Console] Command lazy loading Fixes #8147. Any help regarding wording/formatting would be much appreciated as usual :) Commits ------- e2d445e Minor rewords a6aac8f Break long lines and add blank lines after RST directives 96461f8 Command lazy loading
2 parents 5e4c033 + e2d445e commit 98a0d7e

File tree

2 files changed

+155
-3
lines changed

2 files changed

+155
-3
lines changed

console/commands_as_services.rst

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,75 @@ works! You can call the ``app:sunshine`` command and start logging.
6666

6767
.. caution::
6868

69-
You *do* have access to services in ``configure()``. However, try to avoid doing
70-
any work (e.g. making database queries), as that code will be run, even if you're
71-
using the console to execute a different command.
69+
You *do* have access to services in ``configure()``. However, if your command is
70+
not :ref:`lazy <console-command-service-lazy-loading>`, try to avoid doing any
71+
work (e.g. making database queries), as that code will be run, even if you're using
72+
the console to execute a different command.
73+
74+
.. _console-command-service-lazy-loading:
75+
76+
Lazy Loading
77+
------------
78+
79+
.. versionadded:: 3.4
80+
Support for command lazy loading was introduced in Symfony 3.4.
81+
82+
To make your command lazily loaded, either define its ``$defaultName`` static property::
83+
84+
class SunshineCommand extends Command
85+
{
86+
protected static $defaultName = 'app:sunshine';
87+
88+
// ...
89+
}
90+
91+
Or set the ``command`` attribute on the ``console.command`` tag in your service definition:
92+
93+
.. configuration-block::
94+
95+
.. code-block:: yaml
96+
97+
services:
98+
99+
AppBundle\Command\SunshineCommand:
100+
tags:
101+
- { name: 'console.command', command: 'app:sunshine' }
102+
# ...
103+
104+
.. code-block:: xml
105+
106+
<?xml version="1.0" encoding="UTF-8" ?>
107+
<container xmlns="http://symfony.com/schema/dic/services"
108+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
109+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
110+
111+
<services>
112+
113+
<service id="AppBundle\Command\SunshineCommand">
114+
<tag name="console.command" command="app:sunshine" />
115+
</service>
116+
117+
</services>
118+
</container>
119+
120+
.. code-block:: php
121+
122+
use AppBundle\Command\SunshineCommand;
123+
124+
//...
125+
126+
$container
127+
->register(SunshineCommand::class)
128+
->addTag('console.command', array('command' => 'app:sunshine'))
129+
;
130+
131+
That's it. In one way or another, the ``SunshineCommand`` will be instantiated
132+
only when the ``app:sunshine`` command is actually called.
133+
134+
.. note::
135+
136+
You don't need to call ``setName()`` for configuring the command when it is lazy.
137+
138+
.. caution::
139+
140+
Calling the ``list`` command requires to load all commands, lazy ones included.

console/lazy_commands.rst

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
How to Make Commands Lazily Loaded
2+
==================================
3+
4+
.. versionadded:: 3.4
5+
Support for command lazy loading was introduced in Symfony 3.4.
6+
7+
.. note::
8+
9+
If you are using the Symfony full-stack framework, you are probably looking for
10+
:ref:`lazy loading of commands defined as services <console-command-service-lazy-loading>`
11+
12+
The traditional way of adding commands to your application is to use
13+
:method:`Symfony\\Component\\Console\\Application::add` which expects a
14+
``Command`` instance as argument.
15+
16+
In order to lazy load commands, you need to register an intermediate loader
17+
which will be responsible for returning ``Command`` instances::
18+
19+
use AppBundle\Command\HeavyCommand;
20+
use Symfony\Component\Console\Application;
21+
use Symfony\Component\Console\CommandLoader\FactoryCommmandLoader;
22+
23+
$commandLoader = new FactoryCommandLoader(array(
24+
'app:heavy' => function () { return new HeavyCommand() },
25+
));
26+
27+
$application = new Application();
28+
$application->setCommandLoader($commandLoader);
29+
$application->run();
30+
31+
This way, the ``HeavyCommand`` instance will be created only when the ``app:heavy``
32+
command is actually called.
33+
34+
This example makes use of the built-in
35+
:class:`Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader` class,
36+
but the :method:`Symfony\\Component\\Console\\Application::setCommandLoader`
37+
method accepts any
38+
:class:`Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface`
39+
instance so you can use your own implementation.
40+
41+
Built-in Command Loaders
42+
------------------------
43+
44+
``FactoryCommandLoader``
45+
~~~~~~~~~~~~~~~~~~~~~~~~
46+
47+
The :class:`Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader`
48+
class provides a simple way of getting commands lazily loaded as it takes an
49+
array of ``Command`` factories as only constructor argument::
50+
51+
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
52+
53+
$commandLoader = new FactoryCommandLoader(array(
54+
'app:foo' => function () { return new FooCommand() },
55+
'app:bar' => array(BarCommand::class, 'create'),
56+
));
57+
58+
Factories can be any PHP callable and will be executed each time
59+
:method:`Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader::get`
60+
is called.
61+
62+
``ContainerCommandLoader``
63+
~~~~~~~~~~~~~~~~~~~~~~~~~~
64+
65+
The :class:`Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader`
66+
class can be used to load commands from a PSR-11 container. As such, its
67+
constructor takes a PSR-11 ``ContainerInterface`` implementation as first
68+
argument and a command map as last argument. The command map must be an array
69+
with command names as keys and service identifiers as values::
70+
71+
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
72+
use Symfony\Component\DependencyInjection\ContainerBuilder;
73+
74+
$container = new ContainerBuilder();
75+
$container->register(FooCommand::class, FooCommand::class);
76+
$container->compile();
77+
78+
$commandLoader = new ContainerCommandLoader($container, array(
79+
'app:foo' => FooCommand::class,
80+
));
81+
82+
Like this, executing the ``app:foo`` command will load the ``FooCommand`` service
83+
by calling ``$container->get(FooCommand::class)``.

0 commit comments

Comments
 (0)