How to Make Commands Lazily Loaded

How to Make Commands Lazily Loaded

Note

If you are using the Symfony full-stack framework, you are probably looking for details about creating lazy commands

The traditional way of adding commands to your application is to use [add()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Console/Application.php "Symfony\Component\Console\Application::add()"), which expects a Command instance as an argument.

In order to lazy-load commands, you need to register an intermediate loader which will be responsible for returning Command instances:

  1. use App\Command\HeavyCommand;
  2. use Symfony\Component\Console\Application;
  3. use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
  4. $commandLoader = new FactoryCommandLoader([
  5. 'app:heavy' => function () { return new HeavyCommand(); },
  6. ]);
  7. $application = new Application();
  8. $application->setCommandLoader($commandLoader);
  9. $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 Symfony\Component\Console\CommandLoader\FactoryCommandLoader class, but the [setCommandLoader()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Console/Application.php "Symfony\Component\Console\Application::setCommandLoader()") method accepts any Symfony\Component\Console\CommandLoader\CommandLoaderInterface instance so you can use your own implementation.

Built-in Command Loaders

FactoryCommandLoader

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

  1. use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
  2. $commandLoader = new FactoryCommandLoader([
  3. 'app:foo' => function () { return new FooCommand(); },
  4. 'app:bar' => [BarCommand::class, 'create'],
  5. ]);

Factories can be any PHP callable and will be executed each time [get()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Console/CommandLoader/FactoryCommandLoader.php "Symfony\Component\Console\CommandLoader\FactoryCommandLoader::get()") is called.

ContainerCommandLoader

The 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 its first argument and a command map as its last argument. The command map must be an array with command names as keys and service identifiers as values:

  1. use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
  2. use Symfony\Component\DependencyInjection\ContainerBuilder;
  3. $containerBuilder = new ContainerBuilder();
  4. $containerBuilder->register(FooCommand::class, FooCommand::class);
  5. $containerBuilder->compile();
  6. $commandLoader = new ContainerCommandLoader($containerBuilder, [
  7. 'app:foo' => FooCommand::class,
  8. ]);

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

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.