curl_multi_exec Maximum execution time of 30 seconds exceeded - php

I have the following function, running an array of URLs with cURL:
function multiRequest($urls){
$res = array();
// Create get requests for each URL
$mh = curl_multi_init();
foreach($urls as $i => $url)
{
$ch[$i] = curl_init($url);
curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch[$i], CURLOPT_HEADER, "Accept: application/json");
curl_setopt($ch[$i], CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt ($ch[$i], CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt ($ch[$i], CURLOPT_SSL_VERIFYPEER, 0);
curl_multi_add_handle($mh, $ch[$i]);
}
// Start performing the request
do {
$execReturnValue = curl_multi_exec($mh, $runningHandles);
} while ($execReturnValue == CURLM_CALL_MULTI_PERFORM);
// Loop and continue processing the request
while ($runningHandles && $execReturnValue == CURLM_OK) {
// Wait forever for network
$numberReady = curl_multi_select($mh);
if ($numberReady != -1) {
// Pull in any new data, or at least handle timeouts
do {
$execReturnValue = curl_multi_exec($mh, $runningHandles);
} while ($execReturnValue == CURLM_CALL_MULTI_PERFORM);
}
}
// Check for any errors
if ($execReturnValue != CURLM_OK) {
trigger_error("Curl multi read error $execReturnValue\n", E_USER_WARNING);
}
// Extract the content
foreach($urls as $i => $url)
{
// Check for errors
$curlError = curl_error($ch[$i]);
if($curlError == "") {
$res[$i] = curl_multi_getcontent($ch[$i]);
} else {
print "Curl error on handle $i: $curlError\n";
}
// Remove and close the handle
curl_multi_remove_handle($mh, $ch[$i]);
curl_close($ch[$i]);
}
// Clean up the curl_multi handle
curl_multi_close($mh);
// Print the response data
return $res;
}
I get this error: Maximum execution time of 30 seconds exceeded, the line it points to is $numberReady = curl_multi_select($mh);
The code used to work on PHP version 5.3, after updating to 5.5 the code is no longer working.
I tried setting the limit to more than 30 seconds, it didn't help, also tried ini_set('max_execution_time', 0);
Is there anything wrong with my code? Is there another way to debug it?

This alternative function solved it for me:
function multiRequest($nodes){
//function multiple_threads_request($nodes){
$mh = curl_multi_init();
$curl_array = array();
foreach($nodes as $i => $url) {
$curl_array[$i] = curl_init($url);
curl_setopt($curl_array[$i], CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_array[$i], CURLOPT_HEADER, "Accept: application/json");
curl_setopt($curl_array[$i], CURLOPT_USERPWD, xxxx);
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) {
$res[$url] = curl_multi_getcontent($curl_array[$i]);
}
foreach($nodes as $i => $url) {
curl_multi_remove_handle($mh, $curl_array[$i]);
}
curl_multi_close($mh);
return $res;
}

Related

php lost curl handle reference

At the bottom of the code snippet, I am attempting to remove the curl handle from the multi handle. However PHP reports that it is an invalid curl handle. the curl_close call reports the same thing. I am confused since I have not closed it above that point.
Am i losing it anywhere. I don't see where...
foreach ($urls as $url) {
$request = [];
$request['url'] = $url;
$request['body'] = '';
$request['response_headers'] = [];
$request['curl_handle'] = curl_init();
$url['config'] = json_decode($url['config'], true);
if($url['config']['method'] == 'GET') {
curl_setopt($request['curl_handle'], CURLOPT_HTTPGET, true);
}
curl_setopt($request['curl_handle'], CURLOPT_URL, $url['source_url']);
curl_setopt($request['curl_handle'], CURLOPT_WRITEFUNCTION, function($curl, $body) use (&$request) {
$request['body'] .= $body;
return strlen($body);
});
curl_setopt($request['curl_handle'], CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$request) {
$request['response_headers'][] = $header;
return strlen($header);
});
$followRedirects = boolval($url['config']['follow_redirects']);
curl_setopt($request['curl_handle'], CURLOPT_FOLLOWLOCATION, $followRedirects);
curl_setopt($request['curl_handle'], CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($request['curl_handle'], CURLOPT_TIMEOUT, 120);
curl_setopt($request['curl_handle'], CURLOPT_MAXREDIRS, intval($url['config']['total_redirects']));
curl_setopt($request['curl_handle'], CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_setopt($request['curl_handle'], CURLOPT_MAXFILESIZE, intval($url['config']['max_download']));
curl_setopt($request['curl_handle'], CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($request['curl_handle'], CURLOPT_SSL_VERIFYPEER, false);
$requests[] = &$request;
}
$mh = curl_multi_init();
//add the handles
foreach ($requests as &$request) {
curl_multi_add_handle($mh, $request['curl_handle']);
}
$active = null;
//execute the handles
do {
$mrc = curl_multi_exec($mh, $active);
print('after exec');
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
$mrc = curl_multi_exec($mh, $active);
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
print('performing again');
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
if ($mrc != CURLM_OK) {
print("Curl multi read error $mrc\n");
}
foreach ($requests as &$request) {
processResponse($request);
curl_multi_remove_handle($mh, $request['curl_handle']);
curl_close($request['curl_handle']);
}
curl_multi_close($mh);
Probably you need to change following lines:
foreach ($requests as &$request) {
processResponse($request);
curl_multi_remove_handle($mh, $request['curl_handle']);
curl_close($request['curl_handle']);
}
to
foreach ($requests as &$request) {
processResponse($request);
curl_close($request['curl_handle']);
curl_multi_remove_handle($mh, $request['curl_handle']);
}
The issue was that $request was being copied by reference into $requests, so the same curl_handle was being passed through multi curl and then to close_handle multiple times. The fix was not setting $request by reference into $requests.
I changed :
$requests[] = &$request;
to
$requests[] = $request;
That solved my issue.

PHP from CLI using Curl Multi Exec

In the code below from http://php.net/manual/en/function.curl-multi-init.php
How can I add code before the second request is made (ie sleep(5)) before curl makes the request to twitter)
Regards
<?php
// create both cURL resources
$ch1 = curl_init();
$ch2 = curl_init();
// set URL and other appropriate options
curl_setopt($ch1, CURLOPT_URL, "https://www.google.com");
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_URL, "https://twitter.com");
curl_setopt($ch2, CURLOPT_HEADER, 0);
//create the multiple cURL handle
$mh = curl_multi_init();
//add the two handles
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);
$active = null;
//execute the handles
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
//close the handles
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
?>
I'm no PHP guy or competent programmer for that matter :D Now that disclaimer is out there, here's my solution.
There's probably a much cleaner way to do this but I have limited knowledge of PHP and how to extend classes. For that reason, I decided to use the built-in process control extensions and create a helper function to handle the curl process. I'm sure there are much better programmers out there ready to provide a much cleaner solution though.
<?php
// Helper function
function async_curl($url,$delay){
sleep($delay);
echo "FORK: Getting $url after $delay seconds\n";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
// Mute the return for demonstration purposes.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
$urls = array("http://google.com","http://twitter.com","http://www.facebook.com");
foreach($urls as $url){
// Generate random timeout for demonstration purposes.
$delay = rand(1,20);
// Create a forked child process for each URL
$pid = pcntl_fork();
// Exit if fork failed
if ($pid == -1) {
exit("Error, failed to create a child process for the URL: $url");
// Create a single child process to call the helper function
} else if ($pid == 0) {
echo "MAIN: Forking process for $url\nPID: " .getmypid() . "\tDelay: $delay\n";
async_curl($url,$delay);
exit();
}
}
// Wait for all forked processes to complete before exiting.
while (($pid = pcntl_waitpid(0, $status)) > 0) {
echo "MAIN: Process $pid completed\n";
}
?>

Could not get any response while using curl in PHP

I am fetching the data using the curl in php but as many data are present it returning the 0 output. I am providing my code below.
$result = array();
// multi handle
$mh = curl_multi_init();
$idArr=[2,147,92];
foreach ($idArr as $key => $value) {
$fetchURL = "http://example.com/index.php/rest/V1/categories/".$value."/products/";
//echo $fetchURL.'<br>';
$multiCurl[$key] = curl_init();
curl_setopt($multiCurl[$key], CURLOPT_URL,$fetch_url);
curl_setopt($multiCurl[$key], CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($multiCurl[$key], CURLOPT_HEADER,array("Content-Type: application/json", "Authorization: Bearer " . json_decode($token)));
curl_setopt($multiCurl[$key], CURLOPT_RETURNTRANSFER,true);
curl_multi_add_handle($mh, $multiCurl[$key]);
}
$index=null;
do {
curl_multi_exec($mh,$index);
} while($index > 0);
// get content and remove handles
foreach($multiCurl as $k => $ch) {
$result[$k] = curl_multi_getcontent($ch);
curl_multi_remove_handle($mh, $ch);
}
// close
curl_multi_close($mh);
print_r($result);
Here I have to pass the multiple request and get the result but in this case no result is coming. While I am using simple curl the result is coming. Here my requirement is to reduce the response time.
You are missing the implementation of "curl_multi_select". I have done it for you but not tested. Give it a go
$result = array();
// multi handle
$mh = curl_multi_init();
$multiCurl = array();
$idArr=[2,147,92];
foreach ($idArr as $key => $value) {
$fetchURL = "http://example.com/index.php/rest/V1/categories/".$value."/products/";
//echo $fetchURL.'<br>';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$fetch_url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HEADER,array("Content-Type: application/json", "Authorization: Bearer " . json_decode($token)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
$multiCurl[$key] = $ch;
curl_multi_add_handle($mh, $ch);
}
$active =null;
do {
$mrc = curl_multi_exec($mh,$active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
foreach($multiCurl as $k => $ch) {
$result[$k] = curl_multi_getcontent($ch);
curl_multi_remove_handle($mh, $ch);
}
// close
curl_multi_close($mh);
print_r($result);
I think you can't get response due to set wrong variable in curl option set.Your third line in foreach loop should be like that
curl_setopt($multiCurl[$key], CURLOPT_URL,$fetchURL);
also you write wrong syntex for headers option replace CURLOPT_HEADER by CURLOPT_HTTPHEADER
here is sample code that works perfectly
$idArr=[20,18,21];
$mh = curl_multi_init();
$requests = array();
$curl_objs_arr = [];
foreach ($idArr as $key => $cat) {
$fetchURL = "http:example.com/v2/products?category=".$cat;
$requests[$key] = curl_init($fetchURL);
curl_setopt($requests[$key], CURLOPT_URL,$fetchURL);
curl_setopt($requests[$key], CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($requests[$key], CURLOPT_HTTPHEADER,array("Content-Type: application/json","Authorization: Bearer " . json_decode($token)));
curl_setopt($requests[$key], CURLOPT_RETURNTRANSFER,true);
curl_multi_add_handle($mh, $requests[$key]);
}
$running = null;
do {
curl_multi_exec($mh, $running);
} while($running > 0);
foreach ($requests as $key => $request) {
$result[$key] = curl_multi_getcontent($request);
curl_multi_remove_handle($mh, $request);
}
curl_multi_close($mh);
echo "<pre>";
print_r($result);exit;
set your code by this way, that's solve your problem.

How to capture curl_errno in curl_multi mode?

For some reason curl_errno($value) always returns 0 instead of 6 when I try url like stkovrflow.com. This is a non existing domain. So curl supposed to return 6. But i'm getting 0.
Can someone tell me what's wrong with my code?
This is how I check curl error
if (curl_errno($value) !== 0)
{
$handles[$key]['error_code'] = curl_errno($value);
}
Here is my full code
<?php
protected function curl($url)
{
$mh = curl_multi_init();
$handles = array();
foreach ($url as $link)
{
$handles[$link] = curl_init($link);
curl_setopt($handles[$link], CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($handles[$link], CURLOPT_HEADER, true);
curl_setopt($handles[$link], CURLOPT_RETURNTRANSFER, true);
curl_setopt($handles[$link],CURLOPT_FAILONERROR,true);
curl_setopt($handles[$link], CURLOPT_FOLLOWLOCATION, $this->curlFollowLocation);
curl_setopt($handles[$link], CURLOPT_MAXREDIRS, $this->curlMaxRedirects);
curl_setopt($handles[$link], CURLOPT_TIMEOUT, $this->curlTimeout);
curl_setopt($handles[$link], CURLOPT_USERAGENT, $this->curlUserAgent);
curl_setopt($handles[$link], CURLOPT_AUTOREFERER, true);
curl_multi_add_handle($mh, $handles[$link]);
}
$running = null;
do {
curl_multi_exec($mh, $running);
usleep(200000);
} while ($running > 0);
foreach ($handles as $key => $value)
{
$handles[$key] = false;
$handles[$key]['error_code'] = false;
if (curl_errno($value) !== 0)
{
$handles[$key]['error_code'] = curl_errno($value);
} else {
$response = curl_multi_getcontent($value);
$httpCode = curl_getinfo($value, CURLINFO_HTTP_CODE);
if ( $httpCode != 200 ) {
$handles[$key]['error_code'] = $httpCode;
} else {
$handles[$key]['html'] = $response;
}
}
curl_multi_remove_handle($mh, $value);
curl_close($value);
}
curl_multi_close($mh);
return $handles;
}
Update:
Looks like curl_errno does not work (see bug report) in curl multi mode. Instead we should use curl_multi_info_read. When I use curl_multi_info_read like this
$e_code = curl_multi_info_read($mh);
var_dump($e_code);
this is my var_dump output.
array (size=3)
'msg' => int 1
'result' => int 6
'handle' => resource(7, curl)
As you can it returns 6 correctly. But php doc says
The data the returned resource points to will not survive calling curl_multi_remove_handle().
Unfortunately my script depends on curl_multi_remove_handle(). Any solution?
=======

PHP curl_multi_exec runs once

I'm having trouble creating multiple xml requests using php's curl_multi_exec.
The problem is that the do...while loop containing the curl_multi_exec command runs only once and then quits.
Resources Used:
http://www.phpied.com/simultaneuos-http-requests-in-php-with-curl/
http://php.net/manual/en/function.curl-multi-exec.php/
http://www.rustyrazorblade.com/2008/02/curl_multi_exec/
Take a look at my code:
//Multi handle curl initialization
$mh = curl_multi_init();
//set url
$url = 'my_url';
foreach($latLng as $id => $l) {
$ch[$id] = curl_init();
//$request previously set
//Initialize and set options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
//add to multi_handle
curl_multi_add_handle($mh, $ch[$id]);
}
//Execute the handles
$running = null;
do {
$mrc = curl_multi_exec($mh, $running);
$ready=curl_multi_select($mh);
echo "Ran once\n";
} while ($mrc == CURLM_CALL_MULTI_PERFORM && $ready > 0);
while ($active && $mrc == CURLM_OK) {
if ($curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $running);
echo "Ran again\n";
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
foreach ($mh as $c) {
// HTTP response code
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
// cURL error number
$curl_errno = curl_errno($c);
// cURL error message
$curl_error = curl_error($c);
// output if there was an error
if ($curl_error) {
echo("*** cURL error: ($curl_errno) $curl_error\n");
}
}
//get content and remove handles
foreach ($ch as $c) {
$result[] = curl_multi_getcontent($c);
curl_multi_remove_handle($mh, $c);
}
print_r($result);
//Close curl
curl_multi_close($mh);
}
I know the request is valid because I receive the correct return data when I perform a single curl execution. The problem lies with the curl_multi_exec().
The output I am receiving is "Ran once" followed by the empty arrays of the curl_multi_getcontent() calls. See below:
Ran once
Array
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] =>
[5] =>
[6] =>
[7] =>
[8] =>
)
Any help is greatly appreciated.
You're not setting up the curl options correctly:
Currently, you're setting options on $ch which is your array, you need to be setting the options specifically on the current curl handler, which in your loop is $ch[$id]:
//Initialize and set options
curl_setopt($ch[$id], CURLOPT_URL, $url);
curl_setopt($ch[$id], CURLOPT_HEADER, 0);
curl_setopt($ch[$id], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch[$id], CURLOPT_POST, 1);
curl_setopt($ch[$id], CURLOPT_POSTFIELDS, $request);
change this:
foreach ($mh as $c) {
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
to:
for($i=1;$i<=count($array);$i++){
$code = curl_multi_getcontent($ch[$i]);
assuming $array is the array for your multiple $url.

Categories