Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有自定义信号处理程序的PHP SIGINT无法终止CURL_Php_Curl_Sigint - Fatal编程技术网

具有自定义信号处理程序的PHP SIGINT无法终止CURL

具有自定义信号处理程序的PHP SIGINT无法终止CURL,php,curl,sigint,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://bla

我有一个带有自定义关机处理程序的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);
}

<代码> > p>我已经用了一段时间来搔过头,没有用PCNTL找到任何最佳的解决方案,但是根据您的命令的预期用途,您可以考虑:

  • 使用超时:
    curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,15)
    您可能已经知道,有很多方法可以控制卷曲的行为,包括设置卷曲
  • 使用
    file\u get\u contents()
    而不是curl

对于信号处理代码,可以这样简单:

$w = new EvSignal(SIGINT, function (){
        exit("SIGINT received\n");
});
Ev::run();
电动汽车分机安装的快速总结:
sudo pecl安装电动汽车

您需要通过添加到php cli的php.ini文件来启用扩展名,该文件可能是/etc/php/7.0/cli/php.ini
extension=“ev.so”

如果pecl抱怨缺少phpize,请安装php7.0-dev.

什么工作? 真正起作用的是给整个系统一些空间来发挥它的信号处理魔力。这样的空间似乎是通过启用cURL的进度处理来提供的,同时还设置了一个“userland”进度回调:

解决方案1 似乎进度回调函数中需要“某些东西”。空主体似乎不起作用,因为它可能只是没有给PHP太多时间进行信号处理(核心推测)


解决方案2 将
pcntl\u signal\u dispatch()

...
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
    pcntl_signal_dispatch();
});
...

解决方案3❤ (菲律宾比索7.1+) 使用
pcntl\u async\u信号(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);
}
发生了什么事? 发送
Ctrl+C
命令时,
PHP
尝试在退出之前完成当前操作

为什么我的(OP)代码不退出? 有关更详细的解释,请参见最后的想法

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

您为本练习选择的网站从未加载

如何修复 要修复此问题,请将URL替换为确实加载的内容,例如

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

<?php
declare(ticks=1);

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

pcntl_signal(SIGINT, $shutdownHandler);

while (true) {
    print "$t\n";
    $t++;
}
然后运行:

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

最后的想法 您试图卷曲的站点实际上是在无止境地发送代码。唯一能够停止进程的操作是
CTRL+X
、设置或cURL选项。当您取出pcntl_信号(SIGINT,$shutdownHandler)时,
CTRL+C
的原因起作用
是因为PHP不再承担由内部函数正常关闭的负担。由于PHP不是并发的,当处理程序进入时,它必须等待轮到它执行,因为
cURL
任务永远不会完成,因此会给您留下永无止境的结果。


我希望这有帮助

如果可以升级到
PHP7.1.X
或更高版本,我会使用@smuf发布的
解决方案3
。那是干净的

但是,如果您无法升级,则需要使用变通方法。问题的发生如以下SO线程中所述

PHP在阻塞IO中很忙,无法处理您已自定义处理程序的挂起信号。每当一个信号被触发,并且您有与之相关联的处理程序时,PHP都会对其进行排队。PHP完成当前操作后,将执行处理程序

但不幸的是,在这种情况下,它永远不会有这样的机会,您没有为CURL指定超时,它只是一个PHP无法逃脱的黑洞

因此,如果您可以让一个php进程负责处理
SIGTERM
,而一个子进程必须处理该工作,那么它就可以工作了,因为父进程将能够捕获信号并处理回调,即使子进程被同一个进程占用

下面是一段代码,它演示了相同的功能

<?php
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        declare (ticks = 1);
        //pcntl_async_signals(true);
        $shutdownHandler = function()
        {
            echo 'Exiting' . PHP_EOL;
        };
        pcntl_signal(SIGINT, $shutdownHandler);
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
        echo "hanging now" . PHP_EOL;
        while (true) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
            curl_exec($ch);
            curl_close($ch);
        }
    }

为什么你认为CURL是对信号做出反应,而不是完全忽略它?@IMSoP对不起,我的问题可能有点误导(我已经更新了)。问题的关键是为什么卷曲在这个场景中是不可杀死的。CURL是一个外部的库,所以在操作过程中杀死它是不容易的/可能的。你必须弄清楚如何连接到默认的信号处理程序中才能正确地终止它。我认为答案在于,问题是你的curl调用被阻塞io卡住了,php根本无法连接到你的信号处理程序,而设置处理程序会禁用原来的处理程序together@Jonathan,如果不在
7.0.X
中重新编译代码,则无法执行此操作。看看这个要点是否是您可以接受的解决方案,如果是这样的话,我会发布一个回复谢谢你的详细回复,但是选择一个缓慢加载的URL是问题的关键。如果未定义信号处理程序,则请求将始终被终止,即使它正在进行。当定义了信号处理程序时,这种行为会发生变化,这正是我感兴趣的地方。@Jonathan我详细介绍了为什么它不适用于您的URL。请阅读整个答案。您的代码不会退出,因为cURL没有完成,所以PHP在完成当前操作之前无法退出。从字面上来说就是答案,唯一的解决办法就是更改URL。当你去掉pcntl_信号(SIGINT,$s)时,CTRL+C工作的原因是
<?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);
}
<?php
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        declare (ticks = 1);
        //pcntl_async_signals(true);
        $shutdownHandler = function()
        {
            echo 'Exiting' . PHP_EOL;
        };
        pcntl_signal(SIGINT, $shutdownHandler);
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
        echo "hanging now" . PHP_EOL;
        while (true) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
            curl_exec($ch);
            curl_close($ch);
        }
    }