Як зробити команди лінивого завантаження

Дата оновлення перекладу 2024-05-09

Як зробити команди лінивого завантаження

Note

Якщо ви використовуєте повностековий фреймворк Symfony, то ви скоріше за все шукаєте подробиці про створення команд лінивого завантаження

Такий підхід може мати недоліки, оскільки деякі команди можуть бути доволі складними для інстанціювання. У цьому випадку ви можете скористатися лінивим завантаженням. Зауважте, що ліниве завантаження не є абсолютним. Насправді, деякі команди, такі як 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\Component\Console\Command\LazyCommand:

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',
    [],
    'Це ще одна більш повна форма лінивої команд.',
    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).