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