PHP cURL多错误处理程序

5

我希望在我的错误处理程序中捕获curl的错误和警告,以便它们不会被echo到用户。为了证明所有错误都已被捕获,我将$err_start字符串添加到错误前面。目前这是我的代码片段(已经简化)(在浏览器中运行,而不是cli):

<?php
set_error_handler('handle_errors');
test_curl();
function handle_errors($error_num, $error_str, $error_file, $error_line)
{           
    $err_start = 'caught error'; //to prove that the error has been properly caught
    die("$err_start $error_num, $error_str, $error_file, $error_line<br>");
}           
function test_curl()
{   
    $curl_multi_handle = curl_multi_init();
    $curl_handle1 = curl_init('iamdooooooooooown.com');
    curl_setopt($curl_handle1, CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($curl_multi_handle, $curl_handle1);
    $still_running = 1;
    while($still_running > 0) $multi_errors = curl_multi_exec($curl_multi_handle, $still_running);
    if($multi_errors != CURLM_OK) trigger_error("curl error [$multi_errors]: ".curl_error($curl_multi_handle), E_USER_ERROR);
    if(strlen(curl_error($curl_handle1))) trigger_error("curl error: [".curl_error($curl_handle1)."]", E_USER_ERROR);
    $curl_info = curl_getinfo($curl_handle1); //info for individual requests
    $content = curl_multi_getcontent($curl_handle1);
    curl_multi_remove_handle($curl_multi_handle, $curl_handle1);
    curl_close($curl_handle1);
    curl_multi_close($curl_multi_handle);
}
?>

请注意,我的完整代码有多个并行请求,但是即使只有一个请求,问题仍会出现,如此处所示。请注意,此代码片段中显示的错误处理程序非常基本-我的实际错误处理程序不会在警告或通知上停止运行,因此无需对此进行指导。
现在,如果我尝试curl一个当前已关闭的主机,则成功捕获curl错误,并且我的脚本会停止并显示以下内容:
caught error 256, curl error: [Couldn't resolve host 'iamdooooooooooown.com'], /var/www/proj/test_curl.php, 18

然而,以下警告未被我的错误处理程序捕获,并已被“echo”到页面上:
Warning: (null)(): 3 is not a valid cURL handle resource in Unknown on line 0

我希望能够在我的错误处理程序中捕获这个警告,以便我可以记录下来后续检查。
我发现的一件事是:只有当curl代码在函数内部时,警告才会出现 - 当代码处于最高作用域级别时不会发生。是否可能一个curl全局变量(如)无法在函数范围内访问?
我正在使用PHP版本5.3.2-1ubuntu4.19。
编辑:
更新了代码片段以充分展示错误。
未捕获的警告仅在函数或类方法内部时发生。

你是否启用了 xdebug.scream 设置的 Xdebug? - Álvaro González
我不这么认为。这个方法是否比 set_error_handler() 捕获更多的错误? - mulllhausen
看一下这个答案,它可能会有所帮助:[链接](https://dev59.com/rGkv5IYBdhLWcg3wzkDa#10476589)。 - Ja͢ck
@Jack 谢谢你的测试。我已经更新了代码片段 - 令我惊讶的是,警告只有在代码位于函数或方法中时才会出现! - mulllhausen
你在使用Apache吗?我在CLI中没有收到警告,但是在Apache中肯定会收到。 - mulllhausen
显示剩余2条评论
2个回答

9

我认为你捕获错误的方式有些不对...你可以尝试...

$nodes = array(
        "http://google.com",
        "http://iamdooooooooooown.com",
        "https://gokillyourself.com"
);

echo "<pre>";
print_r(multiplePost($nodes));

输出
Array
(
    [google.com] => #HTTP-OK 48.52 kb returned
    [iamdooooooooooown.com] => #HTTP-ERROR 0  for : http://iamdooooooooooown.com
    [gokillyourself.com] => #HTTP-ERROR 0  for : https://gokillyourself.com
)

使用的函数

function multiplePost($nodes) {
    $mh = curl_multi_init();
    $curl_array = array();
    foreach ( $nodes as $i => $url ) {
        $url = trim($url);
        $curl_array[$i] = curl_init($url);
        curl_setopt($curl_array[$i], CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl_array[$i], CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)');
        curl_setopt($curl_array[$i], CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($curl_array[$i], CURLOPT_TIMEOUT, 15);
        curl_setopt($curl_array[$i], CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl_array[$i], CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($curl_array[$i], CURLOPT_SSL_VERIFYPEER, 0);
        curl_multi_add_handle($mh, $curl_array[$i]);
    }
    $running = NULL;
    do {
        usleep(10000);
        curl_multi_exec($mh, $running);
    } while ( $running > 0 );
    $res = array();

    foreach ( $nodes as $i => $url ) {
        $domain = parse_url($url, PHP_URL_HOST);
        $curlErrorCode = curl_errno($curl_array[$i]);
        if ($curlErrorCode === 0) {
            $info = curl_getinfo($curl_array[$i]);
            $info['url'] = trim($info['url']);
            if ($info['http_code'] == 200) {
                $content = curl_multi_getcontent($curl_array[$i]);
                $res[$domain] = sprintf("#HTTP-OK %0.2f kb returned", strlen($content) / 1024);
            } else {
                $res[$domain] = "#HTTP-ERROR {$info['http_code'] }  for : {$info['url']}";
            }
        } else {
            $res[$domain] = sprintf("#CURL-ERROR %d: %s ", $curlErrorCode, curl_error($curl_array[$i]));
        }
        curl_multi_remove_handle($mh, $curl_array[$i]);
        curl_close($curl_array[$i]);
        flush();
        ob_flush();
    }
    curl_multi_close($mh);
    return $res;
}

感谢您的翻译机器服务,以下是翻译好的内容:我本来以为主机宕机会返回curl错误,但是根据您的代码表现,确实是http错误而不是curl错误。根据您的代码,只有在curl_errno()返回0时才应该调用curl_error()。否则,curl_error()似乎会输出无法抑制的警告信息。尽管如此,当主机宕机时,curl_errno()没有返回52(CURLE_GOT_NOTHING)仍然很奇怪。谢谢! - mulllhausen

2

可能是php-curl的一个bug导致的。当删除下面这行代码时,一切正常:

if(strlen(curl_error($curl_handle1))) trigger_error("curl error: [".curl_error($curl_handle1)."]", E_USER_ERROR);

据我所知,对于一个宕机的主机进行 curl 操作会以某种方式破坏 $curl_handle1,而 curl_error() 函数并未做好处理。为了解决这个问题(在修复此漏洞之前),只需测试由 curl_getinfo() 返回的 http_code 是否为 0。如果是 0,则不要使用 curl_error 函数:
if($multi_errors != CURLM_OK) trigger_error("curl error [$multi_errors]: ".curl_error($curl_multi_handle), E_USER_ERROR);
$curl_info = curl_getinfo($curl_handle1); //info for individual requests
$is_up = ($curl_info['http_code'] == 0) ? 0 : 1;
if($is_up && strlen(curl_error($curl_handle1))) trigger_error("curl error: [".curl_error($curl_handle1)."]", E_USER_ERROR);

这不是一个非常优雅的解决方案,但现在可能只能这样做。


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