5

i want to capture curl errors and warnings in my error handler so that they do not get echoed to the user. to prove that all errors have been caught i prepend the $err_start string to the error. currently here is a working (but simplified) snippet of my code (run it in a browser, not 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);
}
?>

note that my full code has multiple requests in parallel, however the issue still manifests with a single request as shown here. note also that the error handler shown in this code snippet is very basic - my actual error handler will not die on warnings or notices, so no need to school me on this.

now if i try and curl a host which is currently down then i successfully capture the curl error and my script dies with:

caught error 256, curl error: [Couldn't resolve host 'iamdooooooooooown.com'], /var/www/proj/test_curl.php, 18

however the following warning is not caught by my error handler function, and is being echoed to the page:

Warning: (null)(): 3 is not a valid cURL handle resource in Unknown on line 0

i would like to capture this warning in my error handler so that i can log it for later inspection.

one thing i have noticed is that the warning only manifests when the curl code is inside a function - it does not happen when the code is at the highest scope level. is it possible that one of the curl globals (eg CURLM_OK) is not accessible within the scope of the test_curl() function?

i am using PHP Version 5.3.2-1ubuntu4.19

edits

  • updated the code snippet to fully demonstrate the error
  • the uncaptured warning only manifests when inside a function or class method
7
  • Do you have Xdebug with the xdebug.scream setting enabled? Commented Apr 14, 2013 at 23:10
  • i don't think so. does this capture more errors than the set_error_handler()? Commented Apr 14, 2013 at 23:15
  • Take a look at this answer. It may help. Commented Apr 16, 2013 at 23:19
  • @Jack thanks for testing. i have updated the code snippet - to my surprise the warning only manifests when the code is within a function or method! Commented Apr 17, 2013 at 2:35
  • Now it shows only caught error 256, curl error: [Couldn't resolve host 'iamdooooooooooown.com'], /home/xxx/curl.php, 18<br> and no other errors. Commented Apr 17, 2013 at 2:38

2 Answers 2

9
+50

I don't think i agree with the with the way you are capturing the error ... you can try

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

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

Output

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 Used

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;
}
Sign up to request clarification or add additional context in comments.

1 Comment

thanks! i clearly was expecting a down host to return a curl error, but as your code shows, there is a http error, but no curl error. as per your code, curl_error() should only be called if curl_errno() does not return 0. otherwise curl_error() seems to print out warnings which cannot be suppressed. it is strange that curl_errno() does not return 52 (CURLE_GOT_NOTHING) when the host is down though. cheers!
2

it is possible that this is a bug with php-curl. when the following line is removed, then everything behaves ok:

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

as far as i can tell, curling a host that is down is corrupting $curl_handle1 in some way that the curl_error() function is not prepared for. to get around this problem (until a bug fix is made) just test if the http_code returned by curl_getinfo() is 0. if it is 0 then do not use the curl_error function:

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);

its not a very elegant solution, but it may have to do for now.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.