2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
15 #define HTTP_WANT_CURL
18 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
20 #include "zend_interfaces.h"
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"
29 #if defined(HAVE_SPL) && !defined(WONKY)
30 /* SPL doesn't install its headers */
31 extern PHPAPI zend_class_entry
*spl_ce_Countable
;
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)
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)
44 HTTP_EMPTY_ARGS(__destruct
);
45 HTTP_EMPTY_ARGS(reset
);
47 HTTP_BEGIN_ARGS(attach
, 1)
48 HTTP_ARG_OBJ(HttpRequest
, request
, 0)
51 HTTP_BEGIN_ARGS(detach
, 1)
52 HTTP_ARG_OBJ(HttpRequest
, request
, 0)
55 HTTP_EMPTY_ARGS(send
);
56 HTTP_EMPTY_ARGS(socketPerform
);
57 HTTP_EMPTY_ARGS(socketSelect
);
59 HTTP_EMPTY_ARGS(valid
);
60 HTTP_EMPTY_ARGS(current
);
62 HTTP_EMPTY_ARGS(next
);
63 HTTP_EMPTY_ARGS(rewind
);
65 HTTP_EMPTY_ARGS(count
);
67 HTTP_EMPTY_ARGS(getAttachedRequests
);
68 HTTP_EMPTY_ARGS(getFinishedRequests
);
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
)
79 HTTP_REQPOOL_ME(socketPerform
, ZEND_ACC_PROTECTED
)
80 HTTP_REQPOOL_ME(socketSelect
, ZEND_ACC_PROTECTED
)
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
)
89 /* implmenents Countable */
90 HTTP_REQPOOL_ME(count
, ZEND_ACC_PUBLIC
)
92 HTTP_REQPOOL_ME(getAttachedRequests
, ZEND_ACC_PUBLIC
)
93 HTTP_REQPOOL_ME(getFinishedRequests
, ZEND_ACC_PUBLIC
)
97 static zend_object_handlers http_requestpool_object_handlers
;
99 PHP_MINIT_FUNCTION(http_requestpool_object
)
101 HTTP_REGISTER_CLASS_EX(HttpRequestPool
, http_requestpool_object
, NULL
, 0);
102 http_requestpool_object_handlers
.clone_obj
= NULL
;
104 #if defined(HAVE_SPL) && !defined(WONKY)
105 zend_class_implements(http_requestpool_object_ce TSRMLS_CC
, 2, spl_ce_Countable
, zend_ce_iterator
);
107 zend_class_implements(http_requestpool_object_ce TSRMLS_CC
, 1, zend_ce_iterator
);
113 zend_object_value
_http_requestpool_object_new(zend_class_entry
*ce TSRMLS_DC
)
115 zend_object_value ov
;
116 http_requestpool_object
*o
;
118 o
= ecalloc(1, sizeof(http_requestpool_object
));
121 http_request_pool_init(&o
->pool
);
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
*));
127 ov
.handle
= putObject(http_requestpool_object
, o
);
128 ov
.handlers
= &http_requestpool_object_handlers
;
133 void _http_requestpool_object_free(zend_object
*object TSRMLS_DC
)
135 http_requestpool_object
*o
= (http_requestpool_object
*) object
;
137 http_request_pool_dtor(&o
->pool
);
141 #define http_requestpool_object_llist2array _http_requestpool_object_llist2array
142 static void _http_requestpool_object_llist2array(zval
**req
, zval
*array TSRMLS_DC
)
145 add_next_index_zval(array
, *req
);
148 /* ### USERLAND ### */
150 /* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]])
152 * Instantiate a new HttpRequestPool object. An HttpRequestPool is
153 * able to send several HttpRequests in parallel.
155 * WARNING: Don't attach/detach HttpRequest objects to the HttpRequestPool
156 * object while you're using the implemented Iterator interface.
158 * Accepts virtual infinite optional parameters each referencing an
159 * HttpRequest object.
161 * Throws HttpRequestPoolException (HttpRequestException, HttpInvalidParamException).
167 * $pool = new HttpRequestPool(
168 * new HttpRequest('http://www.google.com/', HttpRequest::METH_HEAD),
169 * new HttpRequest('http://www.php.net/', HttpRequest::METH_HEAD)
172 * foreach($pool as $request) {
173 * printf("%s is %s (%d)\n",
174 * $request->getUrl(),
175 * $request->getResponseCode() ? 'alive' : 'not alive',
176 * $request->getResponseCode()
179 * } catch (HttpException $e) {
185 PHP_METHOD(HttpRequestPool
, __construct
)
187 int argc
= ZEND_NUM_ARGS();
188 zval
***argv
= safe_emalloc(argc
, sizeof(zval
*), 0);
189 getObject(http_requestpool_object
, obj
);
192 if (SUCCESS
== zend_get_parameters_array_ex(argc
, argv
)) {
195 for (i
= 0; i
< argc
; ++i
) {
196 if (Z_TYPE_PP(argv
[i
]) == IS_OBJECT
&& instanceof_function(Z_OBJCE_PP(argv
[i
]), http_request_object_ce TSRMLS_CC
)) {
197 http_request_pool_try
{
198 http_request_pool_attach(&obj
->pool
, *(argv
[i
]));
199 } http_request_pool_catch();
202 http_request_pool_final();
209 /* {{{ proto void HttpRequestPool::__destruct()
211 * Clean up HttpRequestPool object.
213 PHP_METHOD(HttpRequestPool
, __destruct
)
215 getObject(http_requestpool_object
, obj
);
219 http_request_pool_detach_all(&obj
->pool
);
223 /* {{{ proto void HttpRequestPool::reset()
225 * Detach all attached HttpRequest objects.
227 PHP_METHOD(HttpRequestPool
, reset
)
229 getObject(http_requestpool_object
, obj
);
233 obj
->iterator
.pos
= 0;
234 http_request_pool_detach_all(&obj
->pool
);
237 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
239 * Attach an HttpRequest object to this HttpRequestPool.
240 * WARNING: set all options prior attaching!
242 * Expects the parameter to be an HttpRequest object not already attached to
243 * antother HttpRequestPool object.
245 * Returns TRUE on success, or FALSE on failure.
247 * Throws HttpInvalidParamException, HttpRequestException,
248 * HttpRequestPoolException, HttpEncodingException.
250 PHP_METHOD(HttpRequestPool
, attach
)
253 STATUS status
= FAILURE
;
254 getObject(http_requestpool_object
, obj
);
257 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "O", &request
, http_request_object_ce
)) {
258 if (obj
->iterator
.pos
> 0 && obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
)) {
259 http_error(HE_THROW
, HTTP_E_REQUEST_POOL
, "Cannot attach to the HttpRequestPool while the iterator is active");
261 status
= http_request_pool_attach(&obj
->pool
, request
);
265 RETURN_SUCCESS(status
);
269 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
271 * Detach an HttpRequest object from this HttpRequestPool.
273 * Expects the parameter to be an HttpRequest object attached to this
274 * HttpRequestPool object.
276 * Returns TRUE on success, or FALSE on failure.
278 * Throws HttpInvalidParamException, HttpRequestPoolException.
280 PHP_METHOD(HttpRequestPool
, detach
)
283 STATUS status
= FAILURE
;
284 getObject(http_requestpool_object
, obj
);
287 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "O", &request
, http_request_object_ce
)) {
288 obj
->iterator
.pos
= -1;
289 status
= http_request_pool_detach(&obj
->pool
, request
);
292 RETURN_SUCCESS(status
);
296 /* {{{ proto bool HttpRequestPool::send()
298 * Send all attached HttpRequest objects in parallel.
300 * Returns TRUE on success, or FALSE on failure.
302 * Throws HttpRequestPoolException (HttpSocketException, HttpRequestException, HttpMalformedHeaderException).
304 PHP_METHOD(HttpRequestPool
, send
)
307 getObject(http_requestpool_object
, obj
);
312 status
= http_request_pool_send(&obj
->pool
);
315 RETURN_SUCCESS(status
);
319 /* {{{ proto protected bool HttpRequestPool::socketPerform()
321 * Returns TRUE until each request has finished its transaction.
326 * class MyPool extends HttpRequestPool
328 * public function send()
330 * while ($this->socketPerform()) {
331 * if (!$this->socketSelect()) {
332 * throw new HttpSocketExcpetion;
337 * protected final function socketPerform()
339 * $result = parent::socketPerform();
340 * foreach ($this->getFinishedRequests() as $r) {
342 * // handle response of finished request
350 PHP_METHOD(HttpRequestPool
, socketPerform
)
352 getObject(http_requestpool_object
, obj
);
356 if (0 < http_request_pool_perform(&obj
->pool
)) {
364 /* {{{ proto protected bool HttpRequestPool::socketSelect()
366 * See HttpRequestPool::socketPerform().
368 * Returns TRUE on success, or FALSE on failure.
370 PHP_METHOD(HttpRequestPool
, socketSelect
)
372 getObject(http_requestpool_object
, obj
);
376 RETURN_SUCCESS(http_request_pool_select(&obj
->pool
));
380 /* implements Iterator */
382 /* {{{ proto bool HttpRequestPool::valid()
384 * Implements Iterator::valid().
386 PHP_METHOD(HttpRequestPool
, valid
)
390 if (return_value_used
) {
391 getObject(http_requestpool_object
, obj
);
392 RETURN_BOOL(obj
->iterator
.pos
>= 0 && obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
));
397 /* {{{ proto HttpRequest HttpRequestPool::current()
399 * Implements Iterator::current().
401 PHP_METHOD(HttpRequestPool
, current
)
405 if (return_value_used
) {
407 zval
**current
= NULL
;
408 zend_llist_position lpos
;
409 getObject(http_requestpool_object
, obj
);
411 if (obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
)) {
412 for ( current
= zend_llist_get_first_ex(&obj
->pool
.handles
, &lpos
);
413 current
&& obj
->iterator
.pos
!= pos
++;
414 current
= zend_llist_get_next_ex(&obj
->pool
.handles
, &lpos
));
416 RETURN_OBJECT(*current
, 1);
424 /* {{{ proto int HttpRequestPool::key()
426 * Implements Iterator::key().
428 PHP_METHOD(HttpRequestPool
, key
)
432 if (return_value_used
) {
433 getObject(http_requestpool_object
, obj
);
434 RETURN_LONG(obj
->iterator
.pos
);
439 /* {{{ proto void HttpRequestPool::next()
441 * Implements Iterator::next().
443 PHP_METHOD(HttpRequestPool
, next
)
446 getObject(http_requestpool_object
, obj
);
447 ++(obj
->iterator
.pos
);
452 /* {{{ proto void HttpRequestPool::rewind()
454 * Implements Iterator::rewind().
456 PHP_METHOD(HttpRequestPool
, rewind
)
459 getObject(http_requestpool_object
, obj
);
460 obj
->iterator
.pos
= 0;
465 /* {{{ proto int HttpRequestPool::count()
467 * Implements Countable.
469 * Returns the number of attached HttpRequest objects.
471 PHP_METHOD(HttpRequestPool
, count
)
474 getObject(http_requestpool_object
, obj
);
475 RETURN_LONG((long) zend_llist_count(&obj
->pool
.handles
));
480 /* {{{ proto array HttpRequestPool::getAttachedRequests()
482 * Get attached HttpRequest objects.
484 * Returns an array containing all currently attached HttpRequest objects.
486 PHP_METHOD(HttpRequestPool
, getAttachedRequests
)
488 getObject(http_requestpool_object
, obj
);
492 array_init(return_value
);
493 zend_llist_apply_with_argument(&obj
->pool
.handles
,
494 (llist_apply_with_arg_func_t
) http_requestpool_object_llist2array
,
495 return_value TSRMLS_CC
);
499 /* {{{ proto array HttpRequestPool::getFinishedRequests()
501 * Get attached HttpRequest objects that already have finished their work.
503 * Returns an array containing all attached HttpRequest objects that
504 * already have finished their work.
506 PHP_METHOD(HttpRequestPool
, getFinishedRequests
)
508 getObject(http_requestpool_object
, obj
);
512 array_init(return_value
);
513 zend_llist_apply_with_argument(&obj
->pool
.finished
,
514 (llist_apply_with_arg_func_t
) http_requestpool_object_llist2array
,
515 return_value TSRMLS_CC
);
519 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
526 * vim600: noet sw=4 ts=4 fdm=marker
527 * vim<600: noet sw=4 ts=4