- cleanup
[m6w6/ext-http] / http_requestpool_object.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_CURL
16 #include "php_http.h"
17
18 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
19
20 #include "zend_interfaces.h"
21
22 #include "php_http_api.h"
23 #include "php_http_exception_object.h"
24 #include "php_http_request_api.h"
25 #include "php_http_request_object.h"
26 #include "php_http_request_pool_api.h"
27 #include "php_http_requestpool_object.h"
28
29 #if defined(HAVE_SPL) && !defined(WONKY)
30 /* SPL doesn't install its headers */
31 extern PHPAPI zend_class_entry *spl_ce_Countable;
32 #endif
33
34 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args)
35 #define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, 0)
36 #define HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, HTTP_ARGS(HttpRequestPool, method), visibility)
37
38 HTTP_BEGIN_ARGS(__construct, 0)
39 HTTP_ARG_OBJ(HttpRequest, request0, 0)
40 HTTP_ARG_OBJ(HttpRequest, request1, 0)
41 HTTP_ARG_OBJ(HttpRequest, requestN, 0)
42 HTTP_END_ARGS;
43
44 HTTP_EMPTY_ARGS(__destruct);
45 HTTP_EMPTY_ARGS(reset);
46
47 HTTP_BEGIN_ARGS(attach, 1)
48 HTTP_ARG_OBJ(HttpRequest, request, 0)
49 HTTP_END_ARGS;
50
51 HTTP_BEGIN_ARGS(detach, 1)
52 HTTP_ARG_OBJ(HttpRequest, request, 0)
53 HTTP_END_ARGS;
54
55 HTTP_EMPTY_ARGS(send);
56 HTTP_EMPTY_ARGS(socketPerform);
57 HTTP_EMPTY_ARGS(socketSelect);
58
59 HTTP_EMPTY_ARGS(valid);
60 HTTP_EMPTY_ARGS(current);
61 HTTP_EMPTY_ARGS(key);
62 HTTP_EMPTY_ARGS(next);
63 HTTP_EMPTY_ARGS(rewind);
64
65 HTTP_EMPTY_ARGS(count);
66
67 HTTP_EMPTY_ARGS(getAttachedRequests);
68 HTTP_EMPTY_ARGS(getFinishedRequests);
69
70 zend_class_entry *http_requestpool_object_ce;
71 zend_function_entry http_requestpool_object_fe[] = {
72 HTTP_REQPOOL_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
73 HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
74 HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC)
75 HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC)
76 HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC)
77 HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC)
78
79 HTTP_REQPOOL_ME(socketPerform, ZEND_ACC_PROTECTED)
80 HTTP_REQPOOL_ME(socketSelect, ZEND_ACC_PROTECTED)
81
82 /* implements Iterator */
83 HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC)
84 HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC)
85 HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC)
86 HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC)
87 HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC)
88
89 /* implmenents Countable */
90 HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC)
91
92 HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC)
93 HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC)
94
95 EMPTY_FUNCTION_ENTRY
96 };
97 static zend_object_handlers http_requestpool_object_handlers;
98
99 PHP_MINIT_FUNCTION(http_requestpool_object)
100 {
101 HTTP_REGISTER_CLASS_EX(HttpRequestPool, http_requestpool_object, NULL, 0);
102 http_requestpool_object_handlers.clone_obj = NULL;
103
104 #if defined(HAVE_SPL) && !defined(WONKY)
105 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator);
106 #else
107 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 1, zend_ce_iterator);
108 #endif
109
110 return SUCCESS;
111 }
112
113 zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC)
114 {
115 zend_object_value ov;
116 http_requestpool_object *o;
117
118 o = ecalloc(1, sizeof(http_requestpool_object));
119 o->zo.ce = ce;
120
121 http_request_pool_init(&o->pool);
122
123 ALLOC_HASHTABLE(OBJ_PROP(o));
124 zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0);
125 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
126
127 ov.handle = putObject(http_requestpool_object, o);
128 ov.handlers = &http_requestpool_object_handlers;
129
130 return ov;
131 }
132
133 void _http_requestpool_object_free(zend_object *object TSRMLS_DC)
134 {
135 http_requestpool_object *o = (http_requestpool_object *) object;
136
137 if (OBJ_PROP(o)) {
138 zend_hash_destroy(OBJ_PROP(o));
139 FREE_HASHTABLE(OBJ_PROP(o));
140 }
141 http_request_pool_dtor(&o->pool);
142 efree(o);
143 }
144
145 #define http_requestpool_object_llist2array _http_requestpool_object_llist2array
146 static void _http_requestpool_object_llist2array(zval **req, zval *array TSRMLS_DC)
147 {
148 ZVAL_ADDREF(*req);
149 add_next_index_zval(array, *req);
150 }
151
152 /* ### USERLAND ### */
153
154 /* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]])
155 *
156 * Instantiate a new HttpRequestPool object. An HttpRequestPool is
157 * able to send several HttpRequests in parallel.
158 *
159 * WARNING: Don't attach/detach HttpRequest objects to the HttpRequestPool
160 * object while you're using the implemented Iterator interface.
161 *
162 * Accepts virtual infinite optional parameters each referencing an
163 * HttpRequest object.
164 *
165 * Throws HttpRequestPoolException (HttpRequestException, HttpInvalidParamException).
166 *
167 * Example:
168 * <pre>
169 * <?php
170 * try {
171 * $pool = new HttpRequestPool(
172 * new HttpRequest('http://www.google.com/', HttpRequest::METH_HEAD),
173 * new HttpRequest('http://www.php.net/', HttpRequest::METH_HEAD)
174 * );
175 * $pool->send();
176 * foreach($pool as $request) {
177 * printf("%s is %s (%d)\n",
178 * $request->getUrl(),
179 * $request->getResponseCode() ? 'alive' : 'not alive',
180 * $request->getResponseCode()
181 * );
182 * }
183 * } catch (HttpException $e) {
184 * echo $e;
185 * }
186 * ?>
187 * </pre>
188 */
189 PHP_METHOD(HttpRequestPool, __construct)
190 {
191 int argc = ZEND_NUM_ARGS();
192 zval ***argv = safe_emalloc(argc, sizeof(zval *), 0);
193 getObject(http_requestpool_object, obj);
194
195 SET_EH_THROW_HTTP();
196 if (SUCCESS == zend_get_parameters_array_ex(argc, argv)) {
197 int i;
198
199 for (i = 0; i < argc; ++i) {
200 if (Z_TYPE_PP(argv[i]) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(argv[i]), http_request_object_ce TSRMLS_CC)) {
201 http_request_pool_try {
202 http_request_pool_attach(&obj->pool, *(argv[i]));
203 } http_request_pool_catch();
204 }
205 }
206 http_request_pool_final();
207 }
208 efree(argv);
209 SET_EH_NORMAL();
210 }
211 /* }}} */
212
213 /* {{{ proto void HttpRequestPool::__destruct()
214 *
215 * Clean up HttpRequestPool object.
216 */
217 PHP_METHOD(HttpRequestPool, __destruct)
218 {
219 getObject(http_requestpool_object, obj);
220
221 NO_ARGS;
222
223 http_request_pool_detach_all(&obj->pool);
224 }
225 /* }}} */
226
227 /* {{{ proto void HttpRequestPool::reset()
228 *
229 * Detach all attached HttpRequest objects.
230 */
231 PHP_METHOD(HttpRequestPool, reset)
232 {
233 getObject(http_requestpool_object, obj);
234
235 NO_ARGS;
236
237 obj->iterator.pos = 0;
238 http_request_pool_detach_all(&obj->pool);
239 }
240
241 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
242 *
243 * Attach an HttpRequest object to this HttpRequestPool.
244 * WARNING: set all options prior attaching!
245 *
246 * Expects the parameter to be an HttpRequest object not already attached to
247 * antother HttpRequestPool object.
248 *
249 * Returns TRUE on success, or FALSE on failure.
250 *
251 * Throws HttpInvalidParamException, HttpRequestException,
252 * HttpRequestPoolException, HttpEncodingException.
253 */
254 PHP_METHOD(HttpRequestPool, attach)
255 {
256 zval *request;
257 STATUS status = FAILURE;
258 getObject(http_requestpool_object, obj);
259
260 SET_EH_THROW_HTTP();
261 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
262 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
263 http_error(HE_THROW, HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active");
264 } else {
265 status = http_request_pool_attach(&obj->pool, request);
266 }
267 }
268 SET_EH_NORMAL();
269 RETURN_SUCCESS(status);
270 }
271 /* }}} */
272
273 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
274 *
275 * Detach an HttpRequest object from this HttpRequestPool.
276 *
277 * Expects the parameter to be an HttpRequest object attached to this
278 * HttpRequestPool object.
279 *
280 * Returns TRUE on success, or FALSE on failure.
281 *
282 * Throws HttpInvalidParamException, HttpRequestPoolException.
283 */
284 PHP_METHOD(HttpRequestPool, detach)
285 {
286 zval *request;
287 STATUS status = FAILURE;
288 getObject(http_requestpool_object, obj);
289
290 SET_EH_THROW_HTTP();
291 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
292 obj->iterator.pos = -1;
293 status = http_request_pool_detach(&obj->pool, request);
294 }
295 SET_EH_NORMAL();
296 RETURN_SUCCESS(status);
297 }
298 /* }}} */
299
300 /* {{{ proto bool HttpRequestPool::send()
301 *
302 * Send all attached HttpRequest objects in parallel.
303 *
304 * Returns TRUE on success, or FALSE on failure.
305 *
306 * Throws HttpRequestPoolException (HttpSocketException, HttpRequestException, HttpMalformedHeaderException).
307 */
308 PHP_METHOD(HttpRequestPool, send)
309 {
310 STATUS status;
311 getObject(http_requestpool_object, obj);
312
313 NO_ARGS;
314
315 SET_EH_THROW_HTTP();
316 status = http_request_pool_send(&obj->pool);
317 SET_EH_NORMAL();
318
319 RETURN_SUCCESS(status);
320 }
321 /* }}} */
322
323 /* {{{ proto protected bool HttpRequestPool::socketPerform()
324 *
325 * Returns TRUE until each request has finished its transaction.
326 *
327 * Usage:
328 * <pre>
329 * <?php
330 * class MyPool extends HttpRequestPool
331 * {
332 * public function send()
333 * {
334 * while ($this->socketPerform()) {
335 * if (!$this->socketSelect()) {
336 * throw new HttpSocketExcpetion;
337 * }
338 * }
339 * }
340 *
341 * protected final function socketPerform()
342 * {
343 * $result = parent::socketPerform();
344 * foreach ($this->getFinishedRequests() as $r) {
345 * $this->detach($r);
346 * // handle response of finished request
347 * }
348 * return $result;
349 * }
350 * }
351 * ?>
352 * </pre>
353 */
354 PHP_METHOD(HttpRequestPool, socketPerform)
355 {
356 getObject(http_requestpool_object, obj);
357
358 NO_ARGS;
359
360 if (0 < http_request_pool_perform(&obj->pool)) {
361 RETURN_TRUE;
362 } else {
363 RETURN_FALSE;
364 }
365 }
366 /* }}} */
367
368 /* {{{ proto protected bool HttpRequestPool::socketSelect()
369 *
370 * See HttpRequestPool::socketPerform().
371 *
372 * Returns TRUE on success, or FALSE on failure.
373 */
374 PHP_METHOD(HttpRequestPool, socketSelect)
375 {
376 getObject(http_requestpool_object, obj);
377
378 NO_ARGS;
379
380 RETURN_SUCCESS(http_request_pool_select(&obj->pool));
381 }
382 /* }}} */
383
384 /* implements Iterator */
385
386 /* {{{ proto bool HttpRequestPool::valid()
387 *
388 * Implements Iterator::valid().
389 */
390 PHP_METHOD(HttpRequestPool, valid)
391 {
392 NO_ARGS;
393
394 IF_RETVAL_USED {
395 getObject(http_requestpool_object, obj);
396 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles));
397 }
398 }
399 /* }}} */
400
401 /* {{{ proto HttpRequest HttpRequestPool::current()
402 *
403 * Implements Iterator::current().
404 */
405 PHP_METHOD(HttpRequestPool, current)
406 {
407 NO_ARGS;
408
409 IF_RETVAL_USED {
410 long pos = 0;
411 zval **current = NULL;
412 zend_llist_position lpos;
413 getObject(http_requestpool_object, obj);
414
415 if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
416 for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos);
417 current && obj->iterator.pos != pos++;
418 current = zend_llist_get_next_ex(&obj->pool.handles, &lpos));
419 if (current) {
420 RETURN_OBJECT(*current, 1);
421 }
422 }
423 RETURN_NULL();
424 }
425 }
426 /* }}} */
427
428 /* {{{ proto int HttpRequestPool::key()
429 *
430 * Implements Iterator::key().
431 */
432 PHP_METHOD(HttpRequestPool, key)
433 {
434 NO_ARGS;
435
436 IF_RETVAL_USED {
437 getObject(http_requestpool_object, obj);
438 RETURN_LONG(obj->iterator.pos);
439 }
440 }
441 /* }}} */
442
443 /* {{{ proto void HttpRequestPool::next()
444 *
445 * Implements Iterator::next().
446 */
447 PHP_METHOD(HttpRequestPool, next)
448 {
449 NO_ARGS {
450 getObject(http_requestpool_object, obj);
451 ++(obj->iterator.pos);
452 }
453 }
454 /* }}} */
455
456 /* {{{ proto void HttpRequestPool::rewind()
457 *
458 * Implements Iterator::rewind().
459 */
460 PHP_METHOD(HttpRequestPool, rewind)
461 {
462 NO_ARGS {
463 getObject(http_requestpool_object, obj);
464 obj->iterator.pos = 0;
465 }
466 }
467 /* }}} */
468
469 /* {{{ proto int HttpRequestPool::count()
470 *
471 * Implements Countable.
472 *
473 * Returns the number of attached HttpRequest objects.
474 */
475 PHP_METHOD(HttpRequestPool, count)
476 {
477 NO_ARGS {
478 getObject(http_requestpool_object, obj);
479 RETURN_LONG((long) zend_llist_count(&obj->pool.handles));
480 }
481 }
482 /* }}} */
483
484 /* {{{ proto array HttpRequestPool::getAttachedRequests()
485 *
486 * Get attached HttpRequest objects.
487 *
488 * Returns an array containing all currently attached HttpRequest objects.
489 */
490 PHP_METHOD(HttpRequestPool, getAttachedRequests)
491 {
492 getObject(http_requestpool_object, obj);
493
494 NO_ARGS;
495
496 array_init(return_value);
497 zend_llist_apply_with_argument(&obj->pool.handles,
498 (llist_apply_with_arg_func_t) http_requestpool_object_llist2array,
499 return_value TSRMLS_CC);
500 }
501 /* }}} */
502
503 /* {{{ proto array HttpRequestPool::getFinishedRequests()
504 *
505 * Get attached HttpRequest objects that already have finished their work.
506 *
507 * Returns an array containing all attached HttpRequest objects that
508 * already have finished their work.
509 */
510 PHP_METHOD(HttpRequestPool, getFinishedRequests)
511 {
512 getObject(http_requestpool_object, obj);
513
514 NO_ARGS;
515
516 array_init(return_value);
517 zend_llist_apply_with_argument(&obj->pool.finished,
518 (llist_apply_with_arg_func_t) http_requestpool_object_llist2array,
519 return_value TSRMLS_CC);
520 }
521 /* }}} */
522
523 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
524
525 /*
526 * Local variables:
527 * tab-width: 4
528 * c-basic-offset: 4
529 * End:
530 * vim600: noet sw=4 ts=4 fdm=marker
531 * vim<600: noet sw=4 ts=4
532 */
533