]>
git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/3rdparty/libraries/humble-http-agent/RollingCurl.php
3 Authored by Josh Fraser (www.joshfraser.com)
4 Released under Apache License 2.0
6 Maintained by Alexander Makarov, http://rmcreative.ru/
8 Modified by Keyvan Minoukadeh for the Five Filters project: http://fivefilters.org
12 * Class that represent a single curl request
14 class RollingCurlRequest
{
16 public $url_original = false; // used for tracking redirects
17 public $method = 'GET';
18 public $post_data = null;
19 public $headers = null;
20 public $options = null;
24 * @param string $method
30 function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
32 $this->url_original
= $url;
33 $this->method
= $method;
34 $this->post_data
= $post_data;
35 $this->headers
= $headers;
36 $this->options
= $options;
43 public function set_original_url($url) {
44 $this->url_original
= $url;
49 public function __destruct() {
50 unset($this->url
, $this->url_original
, $this->method
, $this->post_data
, $this->headers
, $this->options
);
55 * RollingCurl custom exception
57 class RollingCurlException
extends Exception
{
61 * Class that holds a rolling queue of curl requests.
63 * @throws RollingCurlException
65 class RollingCurl
implements Countable
{
69 * Window size is the max number of simultaneous connections allowed.
71 * REMEMBER TO RESPECT THE SERVERS:
72 * Sending too many requests at one time can easily be perceived
73 * as a DOS attack. Increase this window_size if you are making requests
74 * to multiple servers or have permission from the receving server admins.
76 private $window_size = 5;
81 * Timeout is the timeout used for curl_multi_select.
83 private $timeout = 10;
88 * Callback function to be applied to each result.
95 * Set your base options that you want to be used with EVERY request.
97 protected $options = array(
98 CURLOPT_SSL_VERIFYPEER
=> 0,
99 CURLOPT_RETURNTRANSFER
=> 1,
100 CURLOPT_CONNECTTIMEOUT
=> 30,
101 CURLOPT_TIMEOUT
=> 30
107 private $headers = array();
114 private $requests = array();
119 * Maps handles to request indexes
121 private $requestMap = array();
125 * Callback function to be applied to each result.
127 * Can be specified as 'my_callback_function'
128 * or array($object, 'my_callback_method').
130 * Function should take three parameters: $response, $info, $request.
131 * $response is response body, $info is additional curl info.
132 * $request is the original request
136 function __construct($callback = null) {
137 $this->callback
= $callback;
141 * @param string $name
144 public function __get($name) {
145 return (isset($this->{$name
})) ? $this->{$name
} : null;
149 * @param string $name
150 * @param mixed $value
153 public function __set($name, $value) {
154 // append the base options & headers
155 if ($name == "options" || $name == "headers") {
156 $this->{$name
} = $value +
$this->{$name
};
158 $this->{$name
} = $value;
164 * Count number of requests added (Countable interface)
168 public function count() {
169 return count($this->requests
);
173 * Add a request to the request queue
175 * @param Request $request
178 public function add($request) {
179 $this->requests
[] = $request;
184 * Create new Request and add it to the request queue
187 * @param string $method
193 public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
194 $this->requests
[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
199 * Perform GET request
206 public function get($url, $headers = null, $options = null) {
207 return $this->request($url, "GET", null, $headers, $options);
211 * Perform POST request
219 public function post($url, $post_data = null, $headers = null, $options = null) {
220 return $this->request($url, "POST", $post_data, $headers, $options);
226 * @param int $window_size Max number of simultaneous connections
227 * @return string|bool
229 public function execute($window_size = null) {
230 // rolling curl window must always be greater than 1
231 if (sizeof($this->requests
) == 1) {
232 return $this->single_curl();
234 // start the rolling curl. window_size is the max number of simultaneous connections
235 return $this->rolling_curl($window_size);
240 * Performs a single curl request
245 private function single_curl() {
247 $request = array_shift($this->requests
);
248 $options = $this->get_options($request);
249 curl_setopt_array($ch, $options);
250 $output = curl_exec($ch);
251 $info = curl_getinfo($ch);
253 // it's not neccesary to set a callback for one-off requests
254 if ($this->callback
) {
255 $callback = $this->callback
;
256 if (is_callable($this->callback
)) {
257 call_user_func($callback, $output, $info, $request);
266 * Performs multiple curl requests
269 * @throws RollingCurlException
270 * @param int $window_size Max number of simultaneous connections
273 private function rolling_curl($window_size = null) {
275 $this->window_size
= $window_size;
277 // make sure the rolling window isn't greater than the # of urls
278 if (sizeof($this->requests
) < $this->window_size
)
279 $this->window_size
= sizeof($this->requests
);
281 if ($this->window_size
< 2) {
282 throw new RollingCurlException("Window size must be greater than 1");
285 $master = curl_multi_init();
287 // start the first batch of requests
288 for ($i = 0; $i < $this->window_size
; $i++
) {
291 $options = $this->get_options($this->requests
[$i]);
293 curl_setopt_array($ch, $options);
294 curl_multi_add_handle($master, $ch);
296 // Add to our request Maps
298 $this->requestMap
[$key] = $i;
302 while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM
) ;
303 if ($execrun != CURLM_OK
)
305 // a request was just completed -- find out which one
306 while ($done = curl_multi_info_read($master)) {
308 // get the info and content returned on the request
309 $info = curl_getinfo($done['handle']);
310 $output = curl_multi_getcontent($done['handle']);
312 // send the return values to the callback function.
313 $callback = $this->callback
;
314 if (is_callable($callback)) {
315 $key = (string) $done['handle'];
316 $request = $this->requests
[$this->requestMap
[$key]];
317 unset($this->requestMap
[$key]);
318 call_user_func($callback, $output, $info, $request);
321 // start a new request (it's important to do this before removing the old one)
322 if ($i < sizeof($this->requests
) && isset($this->requests
[$i]) && $i < count($this->requests
)) {
324 $options = $this->get_options($this->requests
[$i]);
325 curl_setopt_array($ch, $options);
326 curl_multi_add_handle($master, $ch);
328 // Add to our request Maps
330 $this->requestMap
[$key] = $i;
334 // remove the curl handle that just completed
335 curl_multi_remove_handle($master, $done['handle']);
339 // Block for data in / output; error handling is done by curl_multi_exec
340 //if ($running) curl_multi_select($master, $this->timeout);
341 // removing timeout as it causes problems on Windows with PHP 5.3.5 and Curl 7.20.0
342 if ($running) curl_multi_select($master);
345 curl_multi_close($master);
351 * Helper function to set up a new request by setting the appropriate options
354 * @param Request $request
357 private function get_options($request) {
358 // options for this entire curl object
359 $options = $this->__get('options');
360 // We're managing reirects in PHP - allows us to intervene and rewrite/block URLs
361 // before the next request goes out.
362 $options[CURLOPT_FOLLOWLOCATION
] = 0;
363 $options[CURLOPT_MAXREDIRS
] = 0;
364 //if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
365 // $options[CURLOPT_FOLLOWLOCATION] = 1;
366 // $options[CURLOPT_MAXREDIRS] = 5;
368 $headers = $this->__get('headers');
369 // append custom headers for this specific request
370 if ($request->headers
) {
371 $headers = $headers +
$request->headers
;
374 // append custom options for this specific request
375 if ($request->options
) {
376 $options = $request->options +
$options;
379 // set the request URL
380 $options[CURLOPT_URL
] = $request->url
;
383 $options[CURLOPT_HTTPHEADER
] = $headers;
385 // return response headers
386 $options[CURLOPT_HEADER
] = 1;
388 // send HEAD request?
389 if ($request->method
== 'HEAD') {
390 $options[CURLOPT_NOBODY
] = 1;
399 public function __destruct() {
400 unset($this->window_size
, $this->callback
, $this->options
, $this->headers
, $this->requests
);