]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/3rdparty/libraries/humble-http-agent/RollingCurl.php
htmlawed via composer
[github/wallabag/wallabag.git] / inc / 3rdparty / libraries / humble-http-agent / RollingCurl.php
CommitLineData
42c80841
NL
1<?php\r
2/*\r
3Authored by Josh Fraser (www.joshfraser.com)\r
4Released under Apache License 2.0\r
5\r
6Maintained by Alexander Makarov, http://rmcreative.ru/\r
7\r
8Modified by Keyvan Minoukadeh for the Five Filters project: http://fivefilters.org\r
9*/\r
10\r
11/**\r
12 * Class that represent a single curl request\r
13 */\r
14class RollingCurlRequest {\r
15 public $url = false;\r
16 public $url_original = false; // used for tracking redirects\r
17 public $method = 'GET';\r
18 public $post_data = null;\r
19 public $headers = null;\r
20 public $options = null;\r
21\r
22 /**\r
23 * @param string $url\r
24 * @param string $method\r
25 * @param $post_data\r
26 * @param $headers\r
27 * @param $options\r
28 * @return void\r
29 */\r
30 function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {\r
31 $this->url = $url;\r
32 $this->url_original = $url;\r
33 $this->method = $method;\r
34 $this->post_data = $post_data;\r
35 $this->headers = $headers;\r
36 $this->options = $options;\r
37 }\r
38 \r
39 /**\r
40 * @param string $url\r
41 * @return void\r
42 */\r
43 public function set_original_url($url) {\r
44 $this->url_original = $url;\r
45 }\r
46 /**\r
47 * @return void\r
48 */\r
49 public function __destruct() {\r
50 unset($this->url, $this->url_original, $this->method, $this->post_data, $this->headers, $this->options);\r
51 }\r
52}\r
53\r
54/**\r
55 * RollingCurl custom exception\r
56 */\r
57class RollingCurlException extends Exception {\r
58}\r
59\r
60/**\r
61 * Class that holds a rolling queue of curl requests.\r
62 *\r
63 * @throws RollingCurlException\r
64 */\r
65class RollingCurl implements Countable {\r
66 /**\r
67 * @var int\r
68 *\r
69 * Window size is the max number of simultaneous connections allowed.\r
70 *\r
71 * REMEMBER TO RESPECT THE SERVERS:\r
72 * Sending too many requests at one time can easily be perceived\r
73 * as a DOS attack. Increase this window_size if you are making requests\r
74 * to multiple servers or have permission from the receving server admins.\r
75 */\r
76 private $window_size = 5;\r
77\r
78 /**\r
79 * @var float\r
80 *\r
81 * Timeout is the timeout used for curl_multi_select.\r
82 */\r
83 private $timeout = 10;\r
84\r
85 /**\r
86 * @var string|array\r
87 *\r
88 * Callback function to be applied to each result.\r
89 */\r
90 private $callback;\r
91\r
92 /**\r
93 * @var array\r
94 *\r
95 * Set your base options that you want to be used with EVERY request.\r
96 */\r
97 protected $options = array(\r
98 CURLOPT_SSL_VERIFYPEER => 0,\r
99 CURLOPT_RETURNTRANSFER => 1,\r
100 CURLOPT_CONNECTTIMEOUT => 30,\r
101 CURLOPT_TIMEOUT => 30\r
102 );\r
103\r
104 /**\r
105 * @var array\r
106 */\r
107 private $headers = array();\r
108\r
109 /**\r
110 * @var Request[]\r
111 *\r
112 * The request queue\r
113 */\r
114 private $requests = array();\r
115\r
116 /**\r
117 * @var RequestMap[]\r
118 *\r
119 * Maps handles to request indexes\r
120 */\r
121 private $requestMap = array();\r
122\r
123 /**\r
124 * @param $callback\r
125 * Callback function to be applied to each result.\r
126 *\r
127 * Can be specified as 'my_callback_function'\r
128 * or array($object, 'my_callback_method').\r
129 *\r
130 * Function should take three parameters: $response, $info, $request.\r
131 * $response is response body, $info is additional curl info.\r
132 * $request is the original request\r
133 *\r
134 * @return void\r
135 */\r
136 function __construct($callback = null) {\r
137 $this->callback = $callback;\r
138 }\r
139\r
140 /**\r
141 * @param string $name\r
142 * @return mixed\r
143 */\r
144 public function __get($name) {\r
145 return (isset($this->{$name})) ? $this->{$name} : null;\r
146 }\r
147\r
148 /**\r
149 * @param string $name\r
150 * @param mixed $value\r
151 * @return bool\r
152 */\r
153 public function __set($name, $value) {\r
154 // append the base options & headers\r
155 if ($name == "options" || $name == "headers") {\r
156 $this->{$name} = $value + $this->{$name};\r
157 } else {\r
158 $this->{$name} = $value;\r
159 }\r
160 return true;\r
161 }\r
162\r
163 /**\r
164 * Count number of requests added (Countable interface)\r
165 *\r
166 * @return int\r
167 */\r
168 public function count() {\r
169 return count($this->requests);\r
170 } \r
171 \r
172 /**\r
173 * Add a request to the request queue\r
174 *\r
175 * @param Request $request\r
176 * @return bool\r
177 */\r
178 public function add($request) {\r
179 $this->requests[] = $request;\r
180 return true;\r
181 }\r
182\r
183 /**\r
184 * Create new Request and add it to the request queue\r
185 *\r
186 * @param string $url\r
187 * @param string $method\r
188 * @param $post_data\r
189 * @param $headers\r
190 * @param $options\r
191 * @return bool\r
192 */\r
193 public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {\r
194 $this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);\r
195 return true;\r
196 }\r
197\r
198 /**\r
199 * Perform GET request\r
200 *\r
201 * @param string $url\r
202 * @param $headers\r
203 * @param $options\r
204 * @return bool\r
205 */\r
206 public function get($url, $headers = null, $options = null) {\r
207 return $this->request($url, "GET", null, $headers, $options);\r
208 }\r
209\r
210 /**\r
211 * Perform POST request\r
212 *\r
213 * @param string $url\r
214 * @param $post_data\r
215 * @param $headers\r
216 * @param $options\r
217 * @return bool\r
218 */\r
219 public function post($url, $post_data = null, $headers = null, $options = null) {\r
220 return $this->request($url, "POST", $post_data, $headers, $options);\r
221 }\r
222\r
223 /**\r
224 * Execute processing\r
225 *\r
226 * @param int $window_size Max number of simultaneous connections\r
227 * @return string|bool\r
228 */\r
229 public function execute($window_size = null) {\r
230 // rolling curl window must always be greater than 1\r
231 if (sizeof($this->requests) == 1) {\r
232 return $this->single_curl();\r
233 } else {\r
234 // start the rolling curl. window_size is the max number of simultaneous connections\r
235 return $this->rolling_curl($window_size);\r
236 }\r
237 }\r
238\r
239 /**\r
240 * Performs a single curl request\r
241 *\r
242 * @access private\r
243 * @return string\r
244 */\r
245 private function single_curl() {\r
246 $ch = curl_init();\r
247 $request = array_shift($this->requests);\r
248 $options = $this->get_options($request);\r
249 curl_setopt_array($ch, $options);\r
250 $output = curl_exec($ch);\r
251 $info = curl_getinfo($ch);\r
252\r
253 // it's not neccesary to set a callback for one-off requests\r
254 if ($this->callback) {\r
255 $callback = $this->callback;\r
256 if (is_callable($this->callback)) {\r
257 call_user_func($callback, $output, $info, $request);\r
258 }\r
259 }\r
260 else\r
261 return $output;\r
262 return true;\r
263 }\r
264\r
265 /**\r
266 * Performs multiple curl requests\r
267 *\r
268 * @access private\r
269 * @throws RollingCurlException\r
270 * @param int $window_size Max number of simultaneous connections\r
271 * @return bool\r
272 */\r
273 private function rolling_curl($window_size = null) {\r
274 if ($window_size)\r
275 $this->window_size = $window_size;\r
276\r
277 // make sure the rolling window isn't greater than the # of urls\r
278 if (sizeof($this->requests) < $this->window_size)\r
279 $this->window_size = sizeof($this->requests);\r
280\r
281 if ($this->window_size < 2) {\r
282 throw new RollingCurlException("Window size must be greater than 1");\r
283 }\r
284\r
285 $master = curl_multi_init();\r
286\r
287 // start the first batch of requests\r
288 for ($i = 0; $i < $this->window_size; $i++) {\r
289 $ch = curl_init();\r
290\r
291 $options = $this->get_options($this->requests[$i]);\r
292\r
293 curl_setopt_array($ch, $options);\r
294 curl_multi_add_handle($master, $ch);\r
295\r
296 // Add to our request Maps\r
297 $key = (string) $ch;\r
298 $this->requestMap[$key] = $i;\r
299 }\r
300\r
301 do {\r
302 while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;\r
303 if ($execrun != CURLM_OK)\r
304 break;\r
305 // a request was just completed -- find out which one\r
306 while ($done = curl_multi_info_read($master)) {\r
307\r
308 // get the info and content returned on the request\r
309 $info = curl_getinfo($done['handle']);\r
310 $output = curl_multi_getcontent($done['handle']);\r
311\r
312 // send the return values to the callback function.\r
313 $callback = $this->callback;\r
314 if (is_callable($callback)) {\r
315 $key = (string) $done['handle'];\r
316 $request = $this->requests[$this->requestMap[$key]];\r
317 unset($this->requestMap[$key]);\r
318 call_user_func($callback, $output, $info, $request);\r
319 }\r
320\r
321 // start a new request (it's important to do this before removing the old one)\r
322 if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {\r
323 $ch = curl_init();\r
324 $options = $this->get_options($this->requests[$i]);\r
325 curl_setopt_array($ch, $options);\r
326 curl_multi_add_handle($master, $ch);\r
327\r
328 // Add to our request Maps\r
329 $key = (string) $ch;\r
330 $this->requestMap[$key] = $i;\r
331 $i++;\r
332 }\r
333\r
334 // remove the curl handle that just completed\r
335 curl_multi_remove_handle($master, $done['handle']);\r
336\r
337 }\r
338\r
339 // Block for data in / output; error handling is done by curl_multi_exec\r
340 //if ($running) curl_multi_select($master, $this->timeout);\r
341 // removing timeout as it causes problems on Windows with PHP 5.3.5 and Curl 7.20.0\r
342 if ($running) curl_multi_select($master);\r
343\r
344 } while ($running);\r
345 curl_multi_close($master);\r
346 return true;\r
347 }\r
348\r
349\r
350 /**\r
351 * Helper function to set up a new request by setting the appropriate options\r
352 *\r
353 * @access private\r
354 * @param Request $request\r
355 * @return array\r
356 */\r
357 private function get_options($request) {\r
358 // options for this entire curl object\r
359 $options = $this->__get('options');\r
360 // We're managing reirects in PHP - allows us to intervene and rewrite/block URLs\r
361 // before the next request goes out.\r
362 $options[CURLOPT_FOLLOWLOCATION] = 0;\r
363 $options[CURLOPT_MAXREDIRS] = 0;\r
364 //if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {\r
365 // $options[CURLOPT_FOLLOWLOCATION] = 1;\r
366 // $options[CURLOPT_MAXREDIRS] = 5;\r
367 //}\r
368 $headers = $this->__get('headers');\r
369 // append custom headers for this specific request\r
370 if ($request->headers) {\r
371 $headers = $headers + $request->headers;\r
372 }\r
373\r
374 // append custom options for this specific request\r
375 if ($request->options) {\r
376 $options = $request->options + $options;\r
377 }\r
378\r
379 // set the request URL\r
380 $options[CURLOPT_URL] = $request->url;\r
381\r
382 if ($headers) {\r
383 $options[CURLOPT_HTTPHEADER] = $headers;\r
384 }\r
385 // return response headers\r
386 $options[CURLOPT_HEADER] = 1;\r
387 \r
388 // send HEAD request?\r
389 if ($request->method == 'HEAD') {\r
390 $options[CURLOPT_NOBODY] = 1;\r
391 }\r
392\r
393 return $options;\r
394 }\r
395\r
396 /**\r
397 * @return void\r
398 */\r
399 public function __destruct() {\r
400 unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);\r
401 }\r
ec397236 402}