public function execute()
{
$master = curl_multi_init();
foreach ($this->multicurlOptions as $multiOption => $multiValue) {
curl_multi_setopt($master, $multiOption, $multiValue);
}
// start the first batch of requests
$firstBatch = $this->getNextPendingRequests($this->getSimultaneousLimit());
// what a silly "error"
if (count($firstBatch) == 0) {
return;
}
foreach ($firstBatch as $request) {
// setup the curl request, queue it up, and put it in the active array
$ch = curl_init();
$options = $this->prepareRequestOptions($request);
curl_setopt_array($ch, $options);
curl_multi_add_handle($master, $ch);
$this->activeRequests[(int) $ch] = $request;
}
$active = null;
// Use a shorter select timeout when there is something to do between calls
$idleCallback = $this->idleCallback;
$selectTimeout = $idleCallback ? 0.1 : 1.0;
do {
// ensure we're running
$status = curl_multi_exec($master, $active);
// see if there is anything to read
while ($transfer = curl_multi_info_read($master)) {
// get the request object back and put the curl response into it
$key = (int) $transfer['handle'];
$request = $this->activeRequests[$key];
$request->setResponseText(curl_multi_getcontent($transfer['handle']));
$request->setResponseErrno(curl_errno($transfer['handle']));
$request->setResponseError(curl_error($transfer['handle']));
$request->setResponseInfo(curl_getinfo($transfer['handle']));
// remove the request from the list of active requests
unset($this->activeRequests[$key]);
// move the request to the completed set
$this->completedRequests[] = $request;
$this->completedRequestCount++;
// start a new request (it's important to do this before removing the old one)
if ($nextRequest = $this->getNextPendingRequest()) {
// setup the curl request, queue it up, and put it in the active array
$ch = curl_init();
$options = $this->prepareRequestOptions($nextRequest);
curl_setopt_array($ch, $options);
curl_multi_add_handle($master, $ch);
$this->activeRequests[(int) $ch] = $nextRequest;
}
// remove the curl handle that just completed
curl_multi_remove_handle($master, $transfer['handle']);
// if there is a callback, run it
if (is_callable($this->callback)) {
$callback = $this->callback;
$callback($request, $this);
}
// if something was requeued, this will get it running/update our loop check values
$status = curl_multi_exec($master, $active);
}
// Error detection -- this is very, very rare
$err = null;
switch ($status) {
case CURLM_BAD_EASY_HANDLE:
$err = 'CURLM_BAD_EASY_HANDLE';
break;
case CURLM_OUT_OF_MEMORY:
$err = 'CURLM_OUT_OF_MEMORY';
break;
case CURLM_INTERNAL_ERROR:
$err = 'CURLM_INTERNAL_ERROR';
break;
case CURLM_BAD_HANDLE:
$err = 'CURLM_BAD_HANDLE';
break;
}
if ($err) {
throw new \Exception("curl_multi_exec failed with error code ({$status}) const ({$err})");
}
// Block until *something* happens to avoid burning CPU cycles for naught
while (0 == curl_multi_select($master, $selectTimeout) && $idleCallback) {
$idleCallback($this);
}
// see if we're done yet or not
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
curl_multi_close($master);
}