private function _process()
{
// if caching is enabled but path doesn't exist, or is not writable
if ($this->cache !== false && (!is_dir($this->cache['path']) || !is_writable($this->cache['path']))) {
// trigger an error and stop execution
trigger_error('Cache path does not exists or is not writable', E_USER_ERROR);
}
// iterate through the requests to process
foreach ($this->_requests as $index => $request) {
// if callback function is defined but it doesn't exists
if ($request['callback'] != '' && !is_callable($request['callback'])) {
// trigger an error and stop execution
// the check is for when callback functions are defined as methods of a class
trigger_error('Callback function "' . (is_array($request['callback']) ? array_pop($request['callback']) : $request['callback']) . '" does not exist', E_USER_ERROR);
}
// if caching is enabled
if ($this->cache !== false) {
// get the name to be used for the cache file associated with the request
$cache_file = $this->_get_cache_file_name($request);
// if cache file exists and is not expired
if (file_exists($cache_file) && filemtime($cache_file) + $this->cache['lifetime'] > time()) {
// if we have a callback
if ($request['callback'] != '') {
// prepare the arguments to pass to the callback function
$arguments = array_merge(array(unserialize($this->cache['compress'] ? gzuncompress(file_get_contents($cache_file)) : file_get_contents($cache_file))), (array) $request['arguments']);
// feed them as arguments to the callback function
call_user_func_array($request['callback'], $arguments);
// remove this request from the list so it doesn't get processed
unset($this->_requests[$index]);
}
}
}
}
// if there are any requests to process
if (!empty($this->_requests)) {
// initialize the multi handle
// this will allow us to process multiple handles in parallel
$this->_multi_handle = curl_multi_init();
// queue the first batch of requests
// (as many as defined by the "threads" property, or less if there aren't as many requests)
$this->_queue_requests();
// a flag telling the library if there are any requests currently processing
$running = null;
// loop
do {
// get status update
while (($status = curl_multi_exec($this->_multi_handle, $running)) == CURLM_CALL_MULTI_PERFORM) {
}
// if no request has finished yet, keep looping
if ($status != CURLM_OK) {
break;
}
// if a request was just completed, we'll have to find out which one
while ($info = curl_multi_info_read($this->_multi_handle)) {
// get handle of the completed request
$handle = $info['handle'];
// get content associated with the handle
$content = curl_multi_getcontent($handle);
// get the handle's ID
$resource_number = preg_replace('/Resource id #/', '', $handle);
// get the information associated with the request
$request = $this->_running['fh' . $resource_number];
// create a new object in which we will store all the data associated with the handle,
// as properties of this object
$result = new stdClass();
// get information about the request
$result->info = curl_getinfo($handle);
// extend the "info" property with the original URL
$result->info = array('original_url' => $request['url']) + $result->info;
// if request was a POST
if (isset($request['options'][CURLOPT_POSTFIELDS]) && $request['options'][CURLOPT_POSTFIELDS]) {
// put POST parameters in the response
$result->post = $request['options'][CURLOPT_POSTFIELDS];
}
// last request headers
$result->headers['last_request'] = isset($request['options'][CURLINFO_HEADER_OUT]) && $request['options'][CURLINFO_HEADER_OUT] == 1 && isset($result->info['request_header']) ? $this->_parse_headers($result->info['request_header'], true) : '';
// remove request headers information from its previous location
unset($result->info['request_header']);
// get headers (unless we were explicitly told not to)
$result->headers['responses'] = isset($request['options'][CURLOPT_HEADER]) && $request['options'][CURLOPT_HEADER] == 1 ? $this->_parse_headers(substr($content, 0, $result->info['header_size'])) : '';
// get output (unless we were explicitly told not to)
$result->body = !isset($request['options'][CURLOPT_NOBODY]) || $request['options'][CURLOPT_NOBODY] == 0 ? isset($request['options'][CURLOPT_HEADER]) && $request['options'][CURLOPT_HEADER] == 1 ? substr($content, $result->info['header_size']) : $content : '';
// if _htmlentities is set to TRUE, we're not doing a binary transfer and we have a body, run htmlentities() on it
if ($this->_htmlentities && !isset($request['options'][CURLOPT_BINARYTRANSFER]) && $result->body != '') {
// since PHP 5.3.0, htmlentities will return an empty string if the input string contains an
// invalid code unit sequence within the given encoding (utf-8 in our case)
// so take care of that
if (defined(ENT_IGNORE)) {
$result->body = htmlentities($result->body, ENT_IGNORE, 'utf-8');
} else {
htmlentities($result->body);
}
}
// get CURLs response code and associated message
$result->response = array($this->_response_messages[$info['result']], $info['result']);
// if we have a callback
if (isset($request['callback']) && $request['callback'] != '') {
// prepare the arguments to pass to the callback function
$arguments = array_merge(array($result), $request['arguments']);
// feed them as arguments to the callback function
// and save the callback's response, if any
$callback_response = call_user_func_array($request['callback'], $arguments);
// if no callback function, we assume the response is TRUE
} else {
$callback_response = true;
}
// if caching is enabled and the callback function did not return FALSE
if ($this->cache !== false && $callback_response !== false) {
// get the name of the cache file associated with the request
$cache_file = $this->_get_cache_file_name($request);
// cache the result
file_put_contents($cache_file, $this->cache['compress'] ? gzcompress(serialize($result)) : serialize($result));
// set rights on the file
chmod($cache_file, intval($this->cache['chmod'], 8));
}
// if there are more URLs to process, queue the next one
if (!empty($this->_requests)) {
$this->_queue_requests();
}
// remove the handle that we finished processing
// this needs to be done *after* we've already queued a new URL for processing
curl_multi_remove_handle($this->_multi_handle, $handle);
// make sure the handle gets closed
curl_close($handle);
// if we downloaded a file
if (isset($request['options'][CURLOPT_BINARYTRANSFER]) && $request['options'][CURLOPT_BINARYTRANSFER]) {
// close the associated file pointer
fclose($this->_running['fh' . $resource_number]['file_handler']);
}
// we don't need the information associated with this request anymore
unset($this->_running['fh' . $resource_number]);
}
// waits until curl_multi_exec() returns CURLM_CALL_MULTI_PERFORM or until the timeout, whatever happens first
// call usleep() if a select returns -1 - workaround for PHP bug: https://bugs.php.net/bug.php?id=61141
if ($running && curl_multi_select($this->_multi_handle) === -1) {
usleep(100);
}
// as long as there are threads running
} while ($running);
// close the multi curl handle
curl_multi_close($this->_multi_handle);
}
}