Как сделать команды ленивой загрузки
Дата обновления перевода 2024-07-17
Как сделать команды ленивой загрузки
Note
Если вы используете полностековый фреймворк Symfony, то вы скорее всего ищете подробности о создании команд ленивой загрузки
Традиционный способ добавления команд в ваше приложение - это использование
add(), который ожидает
экземпляр Command
в качестве аргумента.
Этот подход может иметь и отрицательные стороны, поскольку некоторые команды могут быть объемными для инстанциирования, и
в этом случае вы можете захотеть загрузить их в ленивом режиме. Однако обратите внимание, что ленивая загрузка
не является абсолютной. Действительно, некоторые команды, такие как list
, help
или _complete
могут
потребовать инициализации других команд, хотя они и являются ленивыми. Например, list
требует
получить название и описание всех команд, что может потребовать инстанциирования команды для получения информации.
Для того, чтобы лениво загружать команды, вам нужно зарегистрировать срединный
загрузчик, который будет отвечать за возвращение экземпляров Command
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use App\Command\HeavyCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
$commandLoader = new FactoryCommandLoader([
// Отметьте, что команда `list` все равно будет инстанциировать эту команду
// в данном примере.
'app:heavy' => static fn(): Command => new HeavyCommand(),
]);
$application = new Application();
$application->setCommandLoader($commandLoader);
$application->run();
Таким образом, экземпляр HeavyCommand
будет создан только тогда, когда
команда app:heavy
будет действительно вызвана.
Этот пример использует встроенный класс FactoryCommandLoader, но метод setCommandLoader() принимает любой экземпляр CommandLoaderInterface, так что вы можете использовать собственные реализации.
Другой способ сделать это - воспользоваться преимуществами Symfony
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
use App\Command\HeavyCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
// В этом случае, хотя команда инстанциируется, низлежащая фабрика команд
// не будет выполнена, если только команда не выполняется по-настоящему, или если кто-то не пытается
// получить доступ к ее определению ввода, чтобы узнать ее вводы аргумента или опции.
$lazyCommand = new LazyCommand(
'app:heavy',
[],
'This is another more complete form of lazy command.',
false,
static fn (): Command => new HeavyCommand(),
);
$application = new Application();
$application->add($lazyCommand);
$application->run();
Встроенные загрузчики команд
FactoryCommandLoader
Класс FactoryCommandLoader
предоставляет простой способ получения команд ленивой загрузки, так как он берёт
массив фабрик Command
в качестве единственного аргумента конструктора:
1 2 3 4 5 6 7
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
$commandLoader = new FactoryCommandLoader([
'app:foo' => function (): Command { return new FooCommand(); },
'app:bar' => [BarCommand::class, 'create'],
]);
Фабрики могут быть любым PHP вызываемым и будут выполнены каждый раз, когда вызывается get().
ContainerCommandLoader
Класс ContainerCommandLoader
может быть использован для загрузки команды из контейнера PSR-11. Таким образом,
её конструктор берёт реализацию PSR-11 ContainerInterface
в качестве своего
первого аргумента, а карту команды - в качестве последнего. Карта команды должна
быть массивом с именами команд в качестве ключей и идентификаторами сервисов в
качестве значений:
1 2 3 4 5 6 7 8 9 10
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, [
'app:foo' => FooCommand::class,
]);
Таким образом, выполнение команды app:foo
загрузит сервис FooCommand
,
вызвав $container->get(FooCommand::class)
.