在CLI中脚本异常后执行代码

8

我希望在PHP脚本中遇到异常时,能够执行一些最终代码。比如说我有一个PHP脚本:

while(true) {
    echo 'loop';
    sleep(1);
}

如果我使用$ php script.php执行脚本,它会在给定的执行时间内运行。

现在我想在脚本被中止后执行一些最终代码。所以如果我

  • 按下Ctrl+C
  • 执行时间结束

在这些情况下是否有可能进行一些清理工作?

我尝试过使用pcntl_signal,但没有成功。还使用了register_shutdown_function,但只有在脚本成功结束时才会调用该函数。

更新

我发现(感谢rch的链接),可以通过以下方式“捕捉”事件:

pcntl_signal(SIGTERM, $restartMyself); // kill
pcntl_signal(SIGHUP,  $restartMyself); // kill -s HUP or kill -1
pcntl_signal(SIGINT,  $restartMyself); // Ctrl-C

但是如果我使用以下代码扩展我的程序:

$cleanUp = function() {
    echo 'clean up';
    exit;
};

pcntl_signal(SIGINT, $cleanUp);

脚本保持执行,但如果我按下Ctrl+C,则不会遵循$cleanUp闭包中的代码。
3个回答

7
函数 pcntl_signal() 是处理脚本被 Ctrl-C(以及其他信号)中断的解决方案。需要注意文档中所说的内容:

必须使用 declare() 声明来指定程序中允许发生回调的位置,以便信号处理程序正常工作。

declare() 语句会安装回调函数,用于处理自上次调用以来接收到的信号分派,并通过调用函数 pcntl_signal_dispatch() 调用您安装的信号处理程序。
此外,您可以在代码流程适当时调用函数 pcntl_signal_dispatch()(完全不使用 declare(ticks=1))。
以下是一个使用了 declare(ticks=1) 的示例程序:
declare(ticks=1);

// Install the signal handlers
pcntl_signal(SIGHUP,  'handleSigHup');
pcntl_signal(SIGINT,  'handleSigInt');
pcntl_signal(SIGTERM, 'handleSigTerm');


while(true) {
    echo 'loop';
    sleep(1);
}

// Reset the signal handlers
pcntl_signal(SIGHUP,  SIG_DFL);
pcntl_signal(SIGINT,  SIG_DFL);
pcntl_signal(SIGTERM, SIG_DFL);



/**
 * SIGHUP: the controlling pseudo or virtual terminal has been closed
 */
function handleSigHup()
{
    echo("Caught SIGHUP, terminating.\n");
    exit(1);
}

/**
 * SIGINT: the user wishes to interrupt the process; this is typically initiated by pressing Control-C
 *
 * It should be noted that SIGINT is nearly identical to SIGTERM.
 */
function handleSigInt()
{
    echo("Caught SIGINT, terminating.\n");
    exit(1);
}

/**
 * SIGTERM: request process termination
 *
 * The SIGTERM signal is a generic signal used to cause program termination.
 * It is the normal way to politely ask a program to terminate.
 * The shell command kill generates SIGTERM by default.
 */
function handleSigTerm()
{
    echo("Caught SIGTERM, terminating.\n");
    exit(1);
}

谢谢!运行得很好。你能告诉我为什么我必须重置信号处理程序吗?我认为这就是我在使用ticks = 1和pcntl_signal一起的方法中错过的东西... - TiMESPLiNTER
1
我不知道重置信号处理程序是否能完成工作。 我之所以放它们在那里是因为习惯,关闭打开的文件、析构构造的对象、释放分配的资源等都是良好的编程实践。 如果您不想立即使用 exit(),但想进行其他处理,则建议在信号处理程序中设置一个标志,并在主程序循环中检查它,然后根据情况进行处理(或者使用 pcntl_signal_dispatch() 而不是 declare(ticks=1))。 您可以阅读有关终止信号的更多信息。 - axiac
再次感谢您的答案和有趣的链接。如果我想使用pcntl_signal_dispatch而不是ticks = 1,我应该在哪里调用它?在我的循环中的某个地方吗?然后脚本执行到分派调用,然后调用由pcntl_signal函数定义的回调,对吗? - TiMESPLiNTER
PHP 安装信号处理程序并将接收到的信号放入队列中。函数 pcntl_signal_dispatch() 从队列中获取信号,并使用 pcntl_signal() 调用您为其提供的处理程序。它在调用时运行(每个循环一次或者在适合您代码的任何时候)。语句 declare(ticks=1) 安装了一个 tick 函数,该函数调用 pcntl_signal_dispatch()。由于 ticks=1,它运行得更频繁,脚本响应更快(但运行速度稍慢)。 - axiac

0

这里可能有一些非常有用的信息,看起来他们正在利用你尝试过的相同方法,但似乎取得了积极的结果?也许这里有一些你没有尝试过或者错过的东西。

在退出时自动重启PHP脚本


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接