setMaxConcurrent($max_concurrent); } public function setMaxConcurrent($max_requests) { if($max_requests > 0) { $this->_maxConcurrent = $max_requests; } } public function setOptions(array $options) { $this->_options = $options; } public function setHeaders(array $headers) { if(is_array($headers) && count($headers)) { $this->_headers = $headers; } } //public function setCallback(callable $callback) { public function setCallback($callback) { $this->_callback = $callback; } /* Bug on php 5.3 Do not use this */ /*public function setTimeout($timeout) { //in milliseconds if($timeout > 0) { $this->_timeout = $timeout/1000; //to seconds } }*/ //Add a request to the request queue public function addRequest( $url, array $post_data = NULL, //callable $callback = NULL, //individual callback rhe //$callback = NULL, //individual callback array $callback = NULL, $user_data = NULL, array $options = NULL, //individual cURL options array $headers = NULL //individual cURL request headers ) { //Add to request queue $this->requests[] = array( 'url' => $url, 'post_data' => ($post_data) ? $post_data : NULL, 'callback' => ($callback) ? $callback : $this->_callback, 'user_data' => ($user_data) ? $user_data : NULL, 'options' => ($options) ? $options : NULL, 'headers' => ($headers) ? $headers : NULL ); return count($this->requests) - 1; //return request number/index } //Reset request queue public function reset() { $this->requests = array(); } //Execute the request queue public function execute() { if(count($this->requests) < $this->_maxConcurrent) { $this->_maxConcurrent = count($this->requests); } //the request map that maps the request queue to request curl handles $requests_map = array(); $multi_handle = curl_multi_init(); //start processing the initial request queue for($i = 0; $i < $this->_maxConcurrent; $i++) { $ch = curl_init(); $request =& $this->requests[$i]; $this->addTimer($request); curl_setopt_array($ch, $this->buildOptions($request)); curl_multi_add_handle($multi_handle, $ch); //add curl handle of a request to the request map $key = (string) $ch; $requests_map[$key] = $i; } do{ while(($mh_status = curl_multi_exec($multi_handle, $active)) == CURLM_CALL_MULTI_PERFORM); if($mh_status != CURLM_OK) { break; } //a request is just completed, find out which one while($completed = curl_multi_info_read($multi_handle)) { $ch = $completed['handle']; $request_info = curl_getinfo($ch); if(curl_errno($ch) !== 0 || intval($request_info['http_code']) !== 200) { //if server responded with http error $response = json_encode(array('error'=>curl_multi_getcontent($ch))); } else { //sucessful response $response = curl_multi_getcontent($ch); } //get request info $key = (string) $ch; $request =& $this->requests[$requests_map[$key]]; //map handler to request index to get request info $url = $request['url']; $callback = $request['callback']; $user_data = $request['user_data']; $options = $request['options']; $this->stopTimer($request); //record request time $time = $request['time']; if($response && (isset($this->_options[CURLOPT_HEADER]) || isset($options[CURLOPT_HEADER]))) { $k = intval($request_info['header_size']); $request_info['response_header'] = substr($response, 0, $k); $response = substr($response, $k); } //remove completed request and its curl handle unset($requests_map[$key]); curl_multi_remove_handle($multi_handle, $ch); //call the callback function and pass request info and user data to it if($callback) { call_user_func($callback, $response, $url, $request_info, $user_data, $time); } $request = NULL; //free up memory now just incase response was large //add/start a new request to the request queue if($i < count($this->requests) && isset($this->requests[$i])) { //if requests left $ch = curl_init(); $request =& $this->requests[$i]; $this->addTimer($request); curl_setopt_array($ch, $this->buildOptions($request)); curl_multi_add_handle($multi_handle, $ch); //add curl handle of a new request to the request map $key = (string) $ch; $requests_map[$key] = $i; $i++; } } if($active) { if(curl_multi_select($multi_handle, $this->_timeout) === -1) { //wait for activity on any connection usleep(5); } } } while ($active || count($requests_map)); //End do-while $this->reset(); curl_multi_close($multi_handle); } //Build individual cURL options for a request private function buildOptions(array $request) { $url = $request['url']; $post_data = $request['post_data']; $individual_opts = $request['options']; $individual_headers = $request['headers']; $options = ($individual_opts) ? $individual_opts + $this->_options : $this->_options; //merge shared and individual request options $headers = ($individual_headers) ? $individual_headers + $this->_headers : $this->_headers; //merge shared and individual request headers //the below will overide the corresponding default or individual options $options[CURLOPT_RETURNTRANSFER] = true; //$options[CURLOPT_TIMEOUT] = $this->_timeout; //timeout in ms. bug in php 5.3 $options[CURLOPT_TIMEOUT] = 5; //timeout in seconds; if($url) { $options[CURLOPT_URL] = $url; } if($headers) { $options[CURLOPT_HTTPHEADER] = $headers; } // enable POST method and set POST parameters if($post_data) { $options[CURLOPT_POST] = 1; $options[CURLOPT_POSTFIELDS] = is_array($post_data)? http_build_query($post_data) : $post_data; } return $options; } private function addTimer(array &$request) { //adds timer object to request $request['timer'] = microtime(true); $request['time'] = false; //default if not overridden by time later } private function stopTimer(array &$request) { $elapsed = microtime(true) - $request['timer']; $request['time'] = $elapsed; unset($request['timer']); return $elapsed; } }