Використання подій

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

Використання подій

Клас додатку компонента Console дозволяє вам за бажанням підключатися до життєвого циклу консольного додатку через події. Замість того, щоб винаходити велосипед, він використовує компонент Symfony EventDispatcher, щоб зробити роботу:

1
2
3
4
5
6
7
8
use Symfony\Component\Console\Application;
use Symfony\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();

$application = new Application();
$application->setDispatcher($dispatcher);
$application->run();

Caution

Події консолі викликаються лише шляхом виконанння основної команди. Команди, викликані основною командою, не оголосять жодної події, якщо вони не запущені самим додатком, див. Як викликати інші команди.

Подія ConsoleEvents::COMMAND

Типове призначення: Зробити щось до запуску будь-якої команди (на кшталт логування, яка команда буде виконана) або відобразити щось про ту подію, яка буде виконана.

Подія ConsoleEvents::COMMAND оголошується прямо перед викликом будь-якої команди. Слухачі отримують подію ConsoleCommandEvent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;

$dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event): void {
    // отримує екземпляр введення
    $input = $event->getInput();

    // отримує екземпляр виведення
    $output = $event->getOutput();

    // отримує команду, яка буде виконана
    $command = $event->getCommand();

    // пише щось про команду
    $output->writeln(sprintf('Before running command <info>%s</info>', $command->getName()));

    // отримує додаток
    $application = $command->getApplication();
});

Відключення команд всередині слухачів

Використовуючи метод disableCommand(), ви можете відключати команди всередині слухача. Тоді додаток не виконуватиме команду, а замість цього поверне код 113 (визначений у ConsoleCommandEvent::RETURN_CODE_DISABLED). Цей код є одним з зарезервованих кодів завершення для команд консолі, які підкорюються стандарту C/C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;

$dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event): void {
    // отримує команду, яка буде виконана
    $command = $event->getCommand();

    // ... перевірити, чи можна виконати команду

    // відключає команду, що призведе до пропуску команди
    // та поверненню коду 113 з додатку
    $event->disableCommand();

    // можливо включити команду у пізнішому слухачі
    if (!$event->commandShouldRun()) {
        $event->enableCommand();
    }
});

Подія ConsoleEvents::ERROR

Типове призначеняя: Обробляти виключення, викликаніпід час виконнання команди.

Кожний раз, коли команда викликає виключення, включно з тими, що запускаються зі слухачів подій, оголошується подія ConsoleEvents::ERROR. Слухач може огорнути або змінити виключення або зробити щось корисне перед тим, як додаток викличе виключення.

Слухачі отримують подію ConsoleErrorEvent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;

$dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event): void {
    $output = $event->getOutput();

    $command = $event->getCommand();

    $output->writeln(sprintf('Oops, exception thrown while running command <info>%s</info>', $command->getName()));

    // отримує поточний код завершення (код виключення)
    $exitCode = $event->getExitCode();

    // змінює виключення на інше
    $event->setException(new \LogicException('Caught exception', $exitCode, $event->getError()));
});

Подія ConsoleEvents::TERMINATE

Типове призначення: Виконає деякі очищувальні дії після виконання команди.

Після виконання команди, оголошується подія ConsoleEvents::TERMINATE. Вона може бути використана для виконанння будь-яких дій, необхідних для всіх команд, або для прибирання того, що ви розпочали у слухачі ConsoleEvents::COMMAND (на кшталт відправки логів, закриття зʼєднання БД, відправки електронних листів, ...). Слухач також може змінити код завершення.

Слухачі отримують подію ConsoleTerminateEvent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;

$dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event): void {
    // отримує виведення
    $output = $event->getOutput();

    // отримує виконану команду
    $command = $event->getCommand();

    // відображає заданий зміст
    $output->writeln(sprintf('After running command <info>%s</info>', $command->getName()));

    // змінює код завершення
    $event->setExitCode(128);
});

Tip

Ця подія також виконується коли команда викликає виключення. Тоді вона виконується прямо піся події ConsoleEvents::ERROR. У цьому випадку, отриманий код завершення є кодом виключення.

Крім того, подія оголошується при виході з команди за сигналом. Детальніше про сигнали можна дізнатися у спеціальному розділі .

Подія ConsoleEvents::SIGNAL

Типове призначення: Для виконання деяких дій після переривання виконання команди.

Сигнали - це асинхронні сповіщення, відправені процесу для того, щоб сповістити його про подію, що відбувалася. Наприклад, коли ви натискаєте Ctrl + C у команді, ОС відправляє їй сигнал SIGINT.

Коли команда переривається, Symfony оголошує подію ConsoleEvents::SIGNAL. Слухайте цю подію, щоб ви могли виконати якісь дії (наприклад, ведення логів якихось результатів, очищення деяких тимчасових файлів і т.д.) до завершення виконання команди.

Слухачі отримують подію ConsoleSignalEvent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleSignalEvent;

$dispatcher->addListener(ConsoleEvents::SIGNAL, function (ConsoleSignalEvent $event): void {

    // отримує число сигналу
    $signal = $event->getHandlingSignal();
    
    // встановлює код виходу
    $event->setExitCode(0);

    if (\SIGINT === $signal) {
        echo "bye bye!";
    }
});

Також можна перервати вихід, якщо ви хочете, щоб команда продовжила своє виконання навіть після того, як подія була оголошена, завдяки методу abortExit()

use SymfonyComponentConsoleConsoleEvents; use SymfonyComponentConsoleEventConsoleSignalEvent;

$dispatcher->addListener(ConsoleEvents::SIGNAL, function (ConsoleSignalEvent $event) {
$event->abortExit();

});

Tip

Всі доступні сигнали (SIGINT, SIGQUIT та ін.) визначені як константи PHP-розширення PCNTL. Щоб ці константи були доступні, необхідно встановити розширення.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// src/Command/SomeCommand.php
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\SignalableCommandInterface;

class SomeCommand extends Command implements SignalableCommandInterface
{
    // ...

    public function getSubscribedSignals(): array
    {
        // тут поверніть будь-який зміст, визначений розширенням PCNTL
        return [\SIGINT, \SIGTERM];
    }

    public function handleSignal(int $signal): int|false
    {
        if (\SIGINT === $signal) {
            // ...
        }

        // ...

        // повернути ціле число для встановлення коду виходу або
        // false, щоб продовжити нормальне виконання
        return 0;

    }
}

Symfony не обробляє жодного сигналу, отриманого командою (навіть SIGKILL, SIGTERM тощо). Така поведінка є цілеспрямованою, оскільки вона надає вам гнучкість для обробки всіх сигналів, наприклад, виконувати деякі завдання перед завершенням команди.

Tip

Якщо вам потрібно отримати назву сигналу з його цілочисельного значення (наприклад, для логування), ви можете скористатися методом getSignalName().