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-2010, 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_EMPTY_ARGS(__construct
);
40 HTTP_EMPTY_ARGS(__destruct
);
41 HTTP_EMPTY_ARGS(reset
);
43 HTTP_BEGIN_ARGS(attach
, 1)
44 HTTP_ARG_OBJ(HttpRequest
, request
, 0)
47 HTTP_BEGIN_ARGS(detach
, 1)
48 HTTP_ARG_OBJ(HttpRequest
, request
, 0)
51 HTTP_EMPTY_ARGS(send
);
52 HTTP_EMPTY_ARGS(socketPerform
);
53 HTTP_BEGIN_ARGS(socketSelect
, 0)
54 HTTP_ARG_VAL(timeout
, 0)
57 HTTP_EMPTY_ARGS(valid
);
58 HTTP_EMPTY_ARGS(current
);
60 HTTP_EMPTY_ARGS(next
);
61 HTTP_EMPTY_ARGS(rewind
);
63 HTTP_EMPTY_ARGS(count
);
65 HTTP_EMPTY_ARGS(getAttachedRequests
);
66 HTTP_EMPTY_ARGS(getFinishedRequests
);
68 HTTP_BEGIN_ARGS(enablePipelining
, 0)
69 HTTP_ARG_VAL(enable
, 0)
72 HTTP_BEGIN_ARGS(enableEvents
, 0)
73 HTTP_ARG_VAL(enable
, 0)
76 zend_class_entry
*http_requestpool_object_ce
;
77 zend_function_entry http_requestpool_object_fe
[] = {
78 HTTP_REQPOOL_ME(__construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
79 HTTP_REQPOOL_ME(__destruct
, ZEND_ACC_PUBLIC
|ZEND_ACC_DTOR
)
80 HTTP_REQPOOL_ME(attach
, ZEND_ACC_PUBLIC
)
81 HTTP_REQPOOL_ME(detach
, ZEND_ACC_PUBLIC
)
82 HTTP_REQPOOL_ME(send
, ZEND_ACC_PUBLIC
)
83 HTTP_REQPOOL_ME(reset
, ZEND_ACC_PUBLIC
)
85 HTTP_REQPOOL_ME(socketPerform
, ZEND_ACC_PROTECTED
)
86 HTTP_REQPOOL_ME(socketSelect
, ZEND_ACC_PROTECTED
)
88 /* implements Iterator */
89 HTTP_REQPOOL_ME(valid
, ZEND_ACC_PUBLIC
)
90 HTTP_REQPOOL_ME(current
, ZEND_ACC_PUBLIC
)
91 HTTP_REQPOOL_ME(key
, ZEND_ACC_PUBLIC
)
92 HTTP_REQPOOL_ME(next
, ZEND_ACC_PUBLIC
)
93 HTTP_REQPOOL_ME(rewind
, ZEND_ACC_PUBLIC
)
95 /* implmenents Countable */
96 HTTP_REQPOOL_ME(count
, ZEND_ACC_PUBLIC
)
98 HTTP_REQPOOL_ME(getAttachedRequests
, ZEND_ACC_PUBLIC
)
99 HTTP_REQPOOL_ME(getFinishedRequests
, ZEND_ACC_PUBLIC
)
101 HTTP_REQPOOL_ME(enablePipelining
, ZEND_ACC_PUBLIC
)
102 HTTP_REQPOOL_ME(enableEvents
, ZEND_ACC_PUBLIC
)
106 static zend_object_handlers http_requestpool_object_handlers
;
108 PHP_MINIT_FUNCTION(http_requestpool_object
)
110 HTTP_REGISTER_CLASS_EX(HttpRequestPool
, http_requestpool_object
, NULL
, 0);
111 http_requestpool_object_handlers
.clone_obj
= NULL
;
113 #if defined(HAVE_SPL) && !defined(WONKY)
114 zend_class_implements(http_requestpool_object_ce TSRMLS_CC
, 2, spl_ce_Countable
, zend_ce_iterator
);
116 zend_class_implements(http_requestpool_object_ce TSRMLS_CC
, 1, zend_ce_iterator
);
122 zend_object_value
_http_requestpool_object_new(zend_class_entry
*ce TSRMLS_DC
)
124 zend_object_value ov
;
125 http_requestpool_object
*o
;
127 o
= ecalloc(1, sizeof(http_requestpool_object
));
130 http_request_pool_init(&o
->pool
);
132 #ifdef ZEND_ENGINE_2_4
133 zend_object_std_init(o
, ce TSRMLS_CC
);
134 object_properties_init(o
, ce
);
136 ALLOC_HASHTABLE(OBJ_PROP(o
));
137 zend_hash_init(OBJ_PROP(o
), zend_hash_num_elements(&ce
->default_properties
), NULL
, ZVAL_PTR_DTOR
, 0);
138 zend_hash_copy(OBJ_PROP(o
), &ce
->default_properties
, (copy_ctor_func_t
) zval_add_ref
, NULL
, sizeof(zval
*));
141 ov
.handle
= putObject(http_requestpool_object
, o
);
142 ov
.handlers
= &http_requestpool_object_handlers
;
147 void _http_requestpool_object_free(zend_object
*object TSRMLS_DC
)
149 http_requestpool_object
*o
= (http_requestpool_object
*) object
;
151 http_request_pool_dtor(&o
->pool
);
155 #define http_requestpool_object_llist2array _http_requestpool_object_llist2array
156 static void _http_requestpool_object_llist2array(zval
**req
, zval
*array TSRMLS_DC
)
159 add_next_index_zval(array
, *req
);
162 /* ### USERLAND ### */
164 /* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]])
165 Creates a new HttpRequestPool object instance. */
166 PHP_METHOD(HttpRequestPool
, __construct
)
168 int argc
= ZEND_NUM_ARGS();
169 zval
***argv
= safe_emalloc(argc
, sizeof(zval
*), 0);
170 getObject(http_requestpool_object
, obj
);
173 if (SUCCESS
== zend_get_parameters_array_ex(argc
, argv
)) {
176 for (i
= 0; i
< argc
; ++i
) {
177 if (Z_TYPE_PP(argv
[i
]) == IS_OBJECT
&& instanceof_function(Z_OBJCE_PP(argv
[i
]), http_request_object_ce TSRMLS_CC
)) {
178 http_request_pool_attach(&obj
->pool
, *(argv
[i
]));
183 http_final(HTTP_EX_CE(request_pool
));
188 /* {{{ proto void HttpRequestPool::__destruct()
189 Clean up HttpRequestPool object. */
190 PHP_METHOD(HttpRequestPool
, __destruct
)
192 getObject(http_requestpool_object
, obj
);
196 http_request_pool_detach_all(&obj
->pool
);
200 /* {{{ proto void HttpRequestPool::reset()
201 Detach all attached HttpRequest objects. */
202 PHP_METHOD(HttpRequestPool
, reset
)
204 getObject(http_requestpool_object
, obj
);
208 obj
->iterator
.pos
= 0;
209 http_request_pool_detach_all(&obj
->pool
);
212 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
213 Attach an HttpRequest object to this HttpRequestPool. WARNING: set all options prior attaching! */
214 PHP_METHOD(HttpRequestPool
, attach
)
217 STATUS status
= FAILURE
;
218 getObject(http_requestpool_object
, obj
);
221 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "O", &request
, http_request_object_ce
)) {
222 if (obj
->iterator
.pos
> 0 && obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
)) {
223 http_error(HE_THROW
, HTTP_E_REQUEST_POOL
, "Cannot attach to the HttpRequestPool while the iterator is active");
225 status
= http_request_pool_attach(&obj
->pool
, request
);
229 RETURN_SUCCESS(status
);
233 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
234 Detach an HttpRequest object from this HttpRequestPool. */
235 PHP_METHOD(HttpRequestPool
, detach
)
238 STATUS status
= FAILURE
;
239 getObject(http_requestpool_object
, obj
);
242 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "O", &request
, http_request_object_ce
)) {
243 obj
->iterator
.pos
= -1;
244 status
= http_request_pool_detach(&obj
->pool
, request
);
247 RETURN_SUCCESS(status
);
251 /* {{{ proto bool HttpRequestPool::send()
252 Send all attached HttpRequest objects in parallel. */
253 PHP_METHOD(HttpRequestPool
, send
)
256 getObject(http_requestpool_object
, obj
);
261 status
= http_request_pool_send(&obj
->pool
);
264 /* rethrow as HttpRequestPoolException */
265 http_final(HTTP_EX_CE(request_pool
));
267 RETURN_SUCCESS(status
);
271 /* {{{ proto protected bool HttpRequestPool::socketPerform()
272 Returns TRUE until each request has finished its transaction. */
273 PHP_METHOD(HttpRequestPool
, socketPerform
)
275 getObject(http_requestpool_object
, obj
);
279 if (0 < http_request_pool_perform(&obj
->pool
)) {
287 /* {{{ proto protected bool HttpRequestPool::socketSelect([double timeout]) */
288 PHP_METHOD(HttpRequestPool
, socketSelect
)
291 struct timeval custom_timeout
, *custom_timeout_ptr
= NULL
;
292 getObject(http_requestpool_object
, obj
);
294 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|d", &timeout
)) {
297 if (ZEND_NUM_ARGS() && timeout
> 0) {
298 custom_timeout
.tv_sec
= (time_t) timeout
;
299 custom_timeout
.tv_usec
= HTTP_USEC(timeout
) % HTTP_MCROSEC
;
300 custom_timeout_ptr
= &custom_timeout
;
303 RETURN_SUCCESS(http_request_pool_select_ex(&obj
->pool
, custom_timeout_ptr
));
307 /* {{{ proto bool HttpRequestPool::valid()
308 Implements Iterator::valid(). */
309 PHP_METHOD(HttpRequestPool
, valid
)
313 if (return_value_used
) {
314 getObject(http_requestpool_object
, obj
);
315 RETURN_BOOL(obj
->iterator
.pos
>= 0 && obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
));
320 /* {{{ proto HttpRequest HttpRequestPool::current()
321 Implements Iterator::current(). */
322 PHP_METHOD(HttpRequestPool
, current
)
326 if (return_value_used
) {
328 zval
**current
= NULL
;
329 zend_llist_position lpos
;
330 getObject(http_requestpool_object
, obj
);
332 if (obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
)) {
333 for ( current
= zend_llist_get_first_ex(&obj
->pool
.handles
, &lpos
);
334 current
&& obj
->iterator
.pos
!= pos
++;
335 current
= zend_llist_get_next_ex(&obj
->pool
.handles
, &lpos
));
337 RETURN_OBJECT(*current
, 1);
345 /* {{{ proto int HttpRequestPool::key()
346 Implements Iterator::key(). */
347 PHP_METHOD(HttpRequestPool
, key
)
351 if (return_value_used
) {
352 getObject(http_requestpool_object
, obj
);
353 RETURN_LONG(obj
->iterator
.pos
);
358 /* {{{ proto void HttpRequestPool::next()
359 Implements Iterator::next(). */
360 PHP_METHOD(HttpRequestPool
, next
)
363 getObject(http_requestpool_object
, obj
);
364 ++(obj
->iterator
.pos
);
369 /* {{{ proto void HttpRequestPool::rewind()
370 Implements Iterator::rewind(). */
371 PHP_METHOD(HttpRequestPool
, rewind
)
374 getObject(http_requestpool_object
, obj
);
375 obj
->iterator
.pos
= 0;
380 /* {{{ proto int HttpRequestPool::count()
381 Implements Countable::count(). */
382 PHP_METHOD(HttpRequestPool
, count
)
385 getObject(http_requestpool_object
, obj
);
386 RETURN_LONG((long) zend_llist_count(&obj
->pool
.handles
));
391 /* {{{ proto array HttpRequestPool::getAttachedRequests()
392 Get attached HttpRequest objects. */
393 PHP_METHOD(HttpRequestPool
, getAttachedRequests
)
395 getObject(http_requestpool_object
, obj
);
399 array_init(return_value
);
400 zend_llist_apply_with_argument(&obj
->pool
.handles
,
401 (llist_apply_with_arg_func_t
) http_requestpool_object_llist2array
,
402 return_value TSRMLS_CC
);
406 /* {{{ proto array HttpRequestPool::getFinishedRequests()
407 Get attached HttpRequest objects that already have finished their work. */
408 PHP_METHOD(HttpRequestPool
, getFinishedRequests
)
410 getObject(http_requestpool_object
, obj
);
414 array_init(return_value
);
415 zend_llist_apply_with_argument(&obj
->pool
.finished
,
416 (llist_apply_with_arg_func_t
) http_requestpool_object_llist2array
,
417 return_value TSRMLS_CC
);
421 /* {{{ proto bool HttpRequestPool::enablePipelining([bool enable = true])
422 Enables pipelining support for all attached requests if support in libcurl is given. */
423 PHP_METHOD(HttpRequestPool
, enablePipelining
)
425 zend_bool enable
= 1;
426 #if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0)
427 getObject(http_requestpool_object
, obj
);
430 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|b", &enable
)) {
433 #if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0)
434 if (CURLM_OK
== curl_multi_setopt(obj
->pool
.ch
, CURLMOPT_PIPELINING
, (long) enable
)) {
442 /* {{{ proto bool HttpRequestPool::enableEvents([bool enable = true])
443 Enables event-driven I/O if support in libcurl is given. */
444 PHP_METHOD(HttpRequestPool
, enableEvents
)
446 zend_bool enable
= 1;
447 #if defined(HTTP_HAVE_EVENT)
448 getObject(http_requestpool_object
, obj
);
451 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|b", &enable
)) {
452 #if defined(HTTP_HAVE_EVENT)
453 obj
->pool
.useevents
= enable
;
461 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
468 * vim600: noet sw=4 ts=4 fdm=marker
469 * vim<600: noet sw=4 ts=4