跳到内容

使用事件

编辑此页

Console 组件的 Application 类允许你通过事件选择性地hook到控制台应用程序的生命周期中。它没有重新发明轮子,而是使用 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();

警告

控制台事件仅由正在执行的主命令触发。由主命令调用的命令将不会触发任何事件,除非由应用程序本身运行,请参阅如何调用其他命令

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 {
    // gets the input instance
    $input = $event->getInput();

    // gets the output instance
    $output = $event->getOutput();

    // gets the command to be executed
    $command = $event->getCommand();

    // writes something about the command
    $output->writeln(sprintf('Before running command <info>%s</info>', $command->getName()));

    // gets the application
    $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 {
    // gets the command to be executed
    $command = $event->getCommand();

    // ... check if the command can be executed

    // disables the command, this will result in the command being skipped
    // and code 113 being returned from the Application
    $event->disableCommand();

    // it is possible to enable the command in a later listener
    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()));

    // gets the current exit code (the exception code)
    $exitCode = $event->getExitCode();

    // changes the exception to another one
    $event->setError(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 {
    // gets the output
    $output = $event->getOutput();

    // gets the command that has been executed
    $command = $event->getCommand();

    // displays the given content
    $output->writeln(sprintf('After running command <info>%s</info>', $command->getName()));

    // changes the exit code
    $event->setExitCode(128);
});

提示

当命令抛出异常时,也会分发此事件。它在 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 {

    // gets the signal number
    $signal = $event->getHandlingSignal();

    // sets the exit code
    $event->setExitCode(0);

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

如果你希望命令即使在事件分发后仍继续执行,也可以使用 abortExit() 方法来中止退出

1
2
3
4
5
6
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleSignalEvent;

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

提示

所有可用的信号(SIGINTSIGQUIT 等)都定义为 PCNTL PHP 扩展的常量。必须安装该扩展才能使这些常量可用。

如果你在 Symfony 应用程序内部使用 Console 组件,则命令可以自行处理信号。为此,请实现 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
// 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
    {
        // return here any of the constants defined by PCNTL extension
        return [\SIGINT, \SIGTERM];
    }

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

        // ...

        // return an integer to set the exit code, or
        // false to continue normal execution
        return 0;
    }
}

Symfony 不处理命令接收到的任何信号(甚至不处理 SIGKILLSIGTERM 等)。此行为是故意的,因为它使你可以灵活地处理所有信号,例如在终止命令之前执行一些任务。

提示

如果你需要从信号的整数值中获取信号名称(例如,用于日志记录),则可以使用 getSignalName() 方法。

这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本