Использование событий

Дата обновления перевода 2023-01-20

Использование событий

Класс Application компонента 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\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleCommandEvent;

$dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
    // получает экземпляр ввода
    $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\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleCommandEvent;

$dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
    // получает команду, которая будет выполнена
    $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\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;

$dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) {
    $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 (вроде отправки логов, закрытия соединения DB, отправки электронных писем, ...). Слушатель также может изменить код завершения.

Слушатели получают событие ConsoleTerminateEvent:

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

$dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) {
    // получает вывод
    $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
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleSignalEvent;

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

    // получает число сигнала
    $signal = $event->getHandlingSignal();

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

Tip

Все доступные сигналы (SIGINT, SIGQUIT, и др.) определены как константы PHP-расширения PCNTL.

Если вы используете компонент Консоль внутри приложения 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
// 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)
    {
        if (\SIGINT === $signal) {
            // ...
        }

        // ...
    }
}