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