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