8000 [Console] Command lazy loading by chalasr · Pull Request #8502 · symfony/symfony-docs · GitHub
[go: up one dir, main page]

Skip to content

[Console] Command lazy loading #8502

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

Merged
merged 3 commits into from
Oct 29, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Command lazy loading
  • Loading branch information
chalasr committed Oct 13, 2017
commit 96461f878637fc4bb05fc99aea70e42c04bfed1b
73 changes: 70 additions & 3 deletions console/commands_as_services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,73 @@ works! You can call the ``app:sunshine`` command and start logging.

.. caution::

You *do* have access to services in ``configure()``. However, try to avoid doing
any work (e.g. making database queries), as that code will be run, even if you're
using the console to execute a different command.
You *do* have access to services in ``configure()``. However, if your command is
not :ref:`lazy <console-command-service-lazy-loading>`, try to avoid doing any
work (e.g. making database queries), as that code will be run, even if you're using
the console to execute a different command.

.. _console-command-service-lazy-loading:

Lazy Loading
------------

.. versionadded:: 3.4
Support for command lazy loading was introduced in Symfony 3.4.

To make your command lazily loaded, either define its ``$defaultName`` static property::

class SunshineCommand extends Command
{
protected static $defaultName = 'app:sunshine';

// ...
}

Or set the ``command`` attribute on the ``console.command`` tag in your service definition:

.. configuration-block::

.. code-block:: yaml

services:

AppBundle\Command\SunshineCommand:
tags:
- { name: 'console.command', command: 'app:sunshine' }
# ...

.. code-block:: xml

<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>

<service id="AppBundle\Command\SunshineCommand">
<tag name="console.command" command="app:sunshine" />
</service>

</services>
</container>

.. code-block:: php

use AppBundle\Command\SunshineCommand;

//...

$container
->register(SunshineCommand::class)
->addTag('console.command', array('command' => 'app:sunshine'))
;

That's it. In one way or another, the ``SunshineCommand`` will be instantiated only when
the ``app:sunshine`` command is actually called.

.. note::
You don't need to call ``setName()`` for configuring the command when it is lazy.

.. caution::
Calling the ``list`` command requires to load all commands, lazy ones included.
81 changes: 81 additions & 0 deletions console/lazy_commands.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
How to Make Commands Lazily Loaded
==================================

.. versionadded:: 3.4
Support for command lazy loading was introduced in Symfony 3.4.

.. note::
If you are using the Symfony full-stack framework, you are probably looking for
:ref:`lazy loading of commands defined as services <console-command-service-lazy-loading>`

The traditional way of adding commands to your application is to use
:method:`Symfony\\Component\\Console\\Application::add` which expects a
``Command`` instance as argument.
In order to get commands loaded lazily, you need to register an intermediate router
which will be responsible for returning ``Command`` instances::

use AppBundle\Command\HeavyCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\CommandLoader\FactoryCommmandLoader;

$commandLoader = new FactoryCommandLoader(array(
'app:heavy' => function () { return new HeavyCommand() },
));

$application = new Application();
$application->setCommandLoader($commandLoader);
$application->run();

This way, the ``HeavyCommand`` instance will be created only when the ``app:heavy``
command is actually called.

This example makes use of the built-in
:class:`Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader` class,
but the :method:`Symfony\\Component\\Console\\Application::setCommandLoader`
method accepts any
:class:`Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface`
instance so you can easily create and use your own implementation.

Built-in Command Loaders
------------------------

``FactoryCommandLoader``
~~~~~~~~~~~~~~~~~~~~~~~~

The :class:`Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader`
class provides a simple way of getting commands lazily loaded as it takes an
array of ``Command`` factories as only constructor argument::

use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;

$commandLoader = new FactoryCommandLoader(array(
'app:foo' => function () { return new FooCommand() },
'app:bar' => array(BarCommand::class, 'create'),
));

Factories can be any PHP callable and will be executed each time
:method:`Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader::get`
is called.

``ContainerCommandLoader``
~~~~~~~~~~~~~~~~~~~~~~~~~~

The :class:`Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader`
class can be used to load commands from a PSR-11 container.
As such, its constructor takes a PSR-11 ``ContainerInterface`` implementation as
first argument and a command map as last argument. The command map must be an array
with command names as keys and service identifiers as values::

use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$container->register(FooCommand::class, FooCommand::class);
$container->compile();

$commandLoader = new ContainerCommandLoader($container, array(
'app:foo' => FooCommand::class,
));

Like this, executing the ``app:foo`` command will load the ``FooCommand`` service
by calling ``$container->get(FooCommand::class)``.
0