使用自定义信号处理程序的PHP SIGINT不能杀死CURL

Jon*_*han 16 php curl sigint

我有一个带有自定义关闭处理程序的PHP命令行应用程序:

<?php
declare(ticks=1);

$shutdownHandler = function () {
    echo 'Exiting';
    exit();
};

pcntl_signal(SIGINT, $shutdownHandler); 

while (true) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
    curl_exec($ch);
    curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)

如果我在进行CURL请求时使用Ctrl+ C终止脚本,则无效.命令只是挂起.如果我删除自定义关闭处理程序,Ctrl+ C立即杀死CURL请求.

为什么在定义SIGINT处理程序时CURL是不可杀死的?

Smu*_*uuf 9

什么工作?

真正有用的是给整个事物一些空间来处理它的信号处理魔法.这样的空间似乎是通过启用cURL的进度处理来提供的,同时还设置了"用户空间"进度回调:

解决方案1

while (true) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_NOPROGRESS, false); // "true" by default
    curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
        usleep(100);
    });
    curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
    curl_exec($ch);
    curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)

好像在进程回调函数中需要有"东西".空体似乎不起作用,因为它可能只是给PHP花费很多时间进行信号处理(硬核猜测).


解决方案2

pcntl_signal_dispatch()回调似乎即使没有工作declare(ticks=1);PHP 7.1.

...
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
    pcntl_signal_dispatch();
});
...
Run Code Online (Sandbox Code Playgroud)

解决方案3❤(PHP 7.1+)

即使使用空进程回调函数体pcntl_async_signals(true),也declare(ticks=1);可以使用代替工作.

这可能是我个人会使用的,所以我将把完整的代码放在这里:

<?php

pcntl_async_signals(true);

$shutdownHandler = function() {
    die("Exiting\n");
};

pcntl_signal(SIGINT, $shutdownHandler);

while (true) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_NOPROGRESS, false);
    curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {});
    curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
    curl_exec($ch);
    curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)

所有这三种解决方案使PHP 7.1击球后几乎立即退出CTRL+C.

  • 这不是"睡觉(100)".这是**'usleep(100)`**.`100`微秒=**`.1`毫秒**. (2认同)

Der*_*ard 8

怎么了?

发送Ctrl + C命令时,PHP尝试在退出之前完成当前操作.

为什么我的(OP)代码不会退出?

在最后看到最终的想法以获得更详细的解释

您的代码不会因为cURL未完成PHP而退出,因此在完成当前操作之前无法退出.

您为此练习选择的网站永远不会加载.

怎么修

要解决,有一些并不加载,如更换URL https://google.com,例如

证明

我编写了自己的代码示例,以准确显示何时/何时PHP决定退出:

<?php
declare(ticks=1);

$t = 1;
$shutdownHandler = function () {
    exit("EXITING NOW\n");
};

pcntl_signal(SIGINT, $shutdownHandler);

while (true) {
    print "$t\n";
    $t++;
}
Run Code Online (Sandbox Code Playgroud)

在终端中运行时,您可以更清楚地了解PHP的运行方式: 在此输入图像描述

在上图中,您可以看到当我通过Ctrl + C(由箭头显示)发出SIGINT命令时,它完成了它正在执行的操作,然后退出.

这意味着如果我的修复是正确的,那么在OP代码中杀死curl所需要的只是一个简单的URL改变:

<?php

declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
    exit("\nEXITING\n");
};

pcntl_signal(SIGINT, $shutdownHandler);


while (true) {
    echo "CURL$t\n";
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://google.com');
    curl_exec($ch);
    curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)

然后运行: 在此输入图像描述

中提琴!正如预期的那样,脚本在当前进程完成后终止,就像它应该的那样.

最后的想法

您尝试卷曲的网站实际上是在无休止的旅行中发送您的代码.能够停止进程的唯一行动是CTRL + X,将最大执行时间设置,或CURLOPT_TIMEOUT卷曲的选择.CTRL+C当你拿出OUT时,原因pcntl_signal(SIGINT, $shutdownHandler);是因为PHP不再有内部函数正常关闭的负担.由于PHP不是并发的,当你确实有处理程序时,它必须在它执行之前等待它 - 它永远不会得到,因为cURL任务永远不会完成,从而留下永无止境的结果.

我希望这有帮助!

  • 感谢您的详细回复,但选择缓慢加载的URL是问题的关键.如果_not_定义了信号处理程序,则始终会终止请求,即使它正在进行中.定义信号处理程序时,此行为会发生变化,这是我感兴趣的. (4认同)