diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index 8edb22e36d2..f9fa33d848a 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -66,6 +66,75 @@ 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 `, 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 + + + + + + + + + + + + + + .. 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. diff --git a/console/lazy_commands.rst b/console/lazy_commands.rst new file mode 100644 index 00000000000..af331a079ba --- /dev/null +++ b/console/lazy_commands.rst @@ -0,0 +1,83 @@ +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 ` + +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 lazy load commands, you need to register an intermediate loader +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 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)``.