From 96461f878637fc4bb05fc99aea70e42c04bfed1b Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Mon, 9 Oct 2017 17:36:32 +0200 Subject: [PATCH 1/3] Command lazy loading --- console/commands_as_services.rst | 73 ++++++++++++++++++++++++++-- console/lazy_commands.rst | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 console/lazy_commands.rst diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index 8edb22e36d2..415f1fec1ad 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -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 `, 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..22506fb82cf --- /dev/null +++ b/console/lazy_commands.rst @@ -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 ` + +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)``. From a6aac8f20ef3d9c804770899747e7a310060b5a1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 13 Oct 2017 14:52:43 +0200 Subject: [PATCH 2/3] Break long lines and add blank lines after RST directives --- console/commands_as_services.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index 415f1fec1ad..f9fa33d848a 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -128,11 +128,13 @@ Or set the ``command`` attribute on the ``console.command`` tag in your service ->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. +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. From e2d445ed816cb451e0c01549357de11bb4789dd6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 13 Oct 2017 14:55:29 +0200 Subject: [PATCH 3/3] Minor rewords --- console/lazy_commands.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/console/lazy_commands.rst b/console/lazy_commands.rst index 22506fb82cf..af331a079ba 100644 --- a/console/lazy_commands.rst +++ b/console/lazy_commands.rst @@ -5,13 +5,15 @@ How to Make Commands Lazily Loaded 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 get commands loaded lazily, you need to register an intermediate router + +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; @@ -34,7 +36,7 @@ This example makes use of the built-in 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. +instance so you can use your own implementation. Built-in Command Loaders ------------------------ @@ -61,9 +63,9 @@ is called. ~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 +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;