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-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
21 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
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"
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
;
39 # include <winsock2.h>
41 #include <curl/curl.h>
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)
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)
53 HTTP_EMPTY_ARGS(__destruct
, 0);
54 HTTP_EMPTY_ARGS(reset
, 0);
56 HTTP_BEGIN_ARGS(attach
, 1)
57 HTTP_ARG_OBJ(HttpRequest
, request
, 0)
60 HTTP_BEGIN_ARGS(detach
, 1)
61 HTTP_ARG_OBJ(HttpRequest
, request
, 0)
64 HTTP_EMPTY_ARGS(send
, 0);
65 HTTP_EMPTY_ARGS(socketPerform
, 0);
66 HTTP_EMPTY_ARGS(socketSelect
, 0);
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);
74 HTTP_EMPTY_ARGS(count
, 0);
76 HTTP_EMPTY_ARGS(getAttachedRequests
, 0);
77 HTTP_EMPTY_ARGS(getFinishedRequests
, 0);
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
);
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
)
91 HTTP_REQPOOL_ME(socketPerform
, ZEND_ACC_PROTECTED
)
92 HTTP_REQPOOL_ME(socketSelect
, ZEND_ACC_PROTECTED
)
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
)
101 /* implmenents Countable */
102 HTTP_REQPOOL_ME(count
, ZEND_ACC_PUBLIC
)
104 HTTP_REQPOOL_ME(getAttachedRequests
, ZEND_ACC_PUBLIC
)
105 HTTP_REQPOOL_ME(getFinishedRequests
, ZEND_ACC_PUBLIC
)
109 static zend_object_handlers http_requestpool_object_handlers
;
111 PHP_MINIT_FUNCTION(http_requestpool_object
)
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
);
117 zend_class_implements(http_requestpool_object_ce TSRMLS_CC
, 1, zend_ce_iterator
);
120 http_requestpool_object_handlers
.clone_obj
= NULL
;
124 zend_object_value
_http_requestpool_object_new(zend_class_entry
*ce TSRMLS_DC
)
126 zend_object_value ov
;
127 http_requestpool_object
*o
;
129 o
= ecalloc(1, sizeof(http_requestpool_object
));
132 http_request_pool_init(&o
->pool
);
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
*));
138 ov
.handle
= putObject(http_requestpool_object
, o
);
139 ov
.handlers
= &http_requestpool_object_handlers
;
144 static inline void _http_requestpool_object_declare_default_properties(TSRMLS_D
)
146 zend_class_entry
*ce
= http_requestpool_object_ce
;
148 DCL_PROP_N(PROTECTED
, pool
);
151 void _http_requestpool_object_free(zend_object
*object TSRMLS_DC
)
153 http_requestpool_object
*o
= (http_requestpool_object
*) object
;
156 zend_hash_destroy(OBJ_PROP(o
));
157 FREE_HASHTABLE(OBJ_PROP(o
));
159 http_request_pool_dtor(&o
->pool
);
163 #define http_requestpool_object_llist2array _http_requestpool_object_llist2array
164 static void _http_requestpool_object_llist2array(zval
**req
, zval
*array TSRMLS_DC
)
167 add_next_index_zval(array
, *req
);
170 /* ### USERLAND ### */
172 /* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]])
174 * Instantiate a new HttpRequestPool object. An HttpRequestPool is
175 * able to send several HttpRequests in parallel.
177 * WARNING: Don't attach/detach HttpRequest objects to the HttpRequestPool
178 * object while you're using the implemented Interator interface.
180 * Accepts virtual infinite optional parameters each referencing an
181 * HttpRequest object.
183 * Throws HttpRequestException, HttpRequestPoolException, HttpInvalidParamException.
189 * $pool = new HttpRequestPool(
190 * new HttpRequest('http://www.google.com/', HttpRequest::METH_HEAD),
191 * new HttpRequest('http://www.php.net/', HttpRequest::METH_HEAD)
194 * foreach($pool as $request) {
195 * printf("%s is %s (%d)\n",
196 * $request->getUrl(),
197 * $request->getResponseCode() ? 'alive' : 'not alive',
198 * $request->getResponseCode()
201 * } catch (HttpException $e) {
207 PHP_METHOD(HttpRequestPool
, __construct
)
209 int argc
= ZEND_NUM_ARGS();
210 zval
***argv
= safe_emalloc(argc
, sizeof(zval
*), 0);
211 getObject(http_requestpool_object
, obj
);
213 if (SUCCESS
== zend_get_parameters_array_ex(argc
, argv
)) {
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
]));
226 /* {{{ proto void HttpRequestPool::__destruct()
228 * Clean up HttpRequestPool object.
230 PHP_METHOD(HttpRequestPool
, __destruct
)
232 getObject(http_requestpool_object
, obj
);
236 http_request_pool_detach_all(&obj
->pool
);
240 /* {{{ proto void HttpRequestPool::reset()
242 * Detach all attached HttpRequest objects.
244 PHP_METHOD(HttpRequestPool
, reset
)
246 getObject(http_requestpool_object
, obj
);
250 obj
->iterator
.pos
= 0;
251 http_request_pool_detach_all(&obj
->pool
);
254 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
256 * Attach an HttpRequest object to this HttpRequestPool.
257 * WARNING: set all options prior attaching!
259 * Expects the parameter to be an HttpRequest object not alread attached to
260 * antother HttpRequestPool object.
262 * Returns TRUE on success, or FALSE on failure.
264 * Throws HttpInvalidParamException, HttpRequestException,
265 * HttpRequestPoolException, HttpEncodingException.
267 PHP_METHOD(HttpRequestPool
, attach
)
270 STATUS status
= FAILURE
;
271 getObject(http_requestpool_object
, obj
);
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");
278 status
= http_request_pool_attach(&obj
->pool
, request
);
282 RETURN_SUCCESS(status
);
286 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
288 * Detach an HttpRequest object from this HttpRequestPool.
290 * Expects the parameter to be an HttpRequest object attached to this
291 * HttpRequestPool object.
293 * Returns TRUE on success, or FALSE on failure.
295 * Throws HttpInvalidParamException, HttpRequestPoolException.
297 PHP_METHOD(HttpRequestPool
, detach
)
300 STATUS status
= FAILURE
;
301 getObject(http_requestpool_object
, obj
);
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
);
309 RETURN_SUCCESS(status
);
313 /* {{{ proto bool HttpRequestPool::send()
315 * Send all attached HttpRequest objects in parallel.
317 * Returns TRUE on success, or FALSE on failure.
319 * Throws HttpSocketException, HttpRequestException,
320 * HttpRequestPoolException, HttpMalformedHeaderException.
322 PHP_METHOD(HttpRequestPool
, send
)
325 getObject(http_requestpool_object
, obj
);
330 status
= http_request_pool_send(&obj
->pool
);
333 RETURN_SUCCESS(status
);
337 /* {{{ proto protected bool HttpRequestPool::socketPerform()
339 * Returns TRUE until each request has finished its transaction.
344 * class MyPool extends HttpRequestPool
346 * public function send()
348 * while ($this->socketPerform()) {
349 * if (!$this->socketSelect()) {
350 * throw new HttpSocketExcpetion;
355 * protected final function socketPerform()
357 * $result = parent::socketPerform();
358 * foreach ($this->getFinishedRequests() as $r) {
360 * // handle response of finished request
368 PHP_METHOD(HttpRequestPool
, socketPerform
)
370 getObject(http_requestpool_object
, obj
);
374 if (0 < http_request_pool_perform(&obj
->pool
)) {
382 /* {{{ proto protected bool HttpRequestPool::socketSelect()
384 * See HttpRequestPool::socketPerform().
386 * Returns TRUE on success, or FALSE on failure.
388 PHP_METHOD(HttpRequestPool
, socketSelect
)
390 getObject(http_requestpool_object
, obj
);
394 RETURN_SUCCESS(http_request_pool_select(&obj
->pool
));
398 /* implements Iterator */
400 /* {{{ proto bool HttpRequestPool::valid()
402 * Implements Iterator::valid().
404 PHP_METHOD(HttpRequestPool
, valid
)
409 getObject(http_requestpool_object
, obj
);
410 RETURN_BOOL(obj
->iterator
.pos
>= 0 && obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
));
415 /* {{{ proto HttpRequest HttpRequestPool::current()
417 * Implements Iterator::current().
419 PHP_METHOD(HttpRequestPool
, current
)
425 zval
**current
= NULL
;
426 zend_llist_position lpos
;
427 getObject(http_requestpool_object
, obj
);
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
));
434 RETURN_OBJECT(*current
);
442 /* {{{ proto int HttpRequestPool::key()
444 * Implements Iterator::key().
446 PHP_METHOD(HttpRequestPool
, key
)
451 getObject(http_requestpool_object
, obj
);
452 RETURN_LONG(obj
->iterator
.pos
);
457 /* {{{ proto void HttpRequestPool::next()
459 * Implements Iterator::next().
461 PHP_METHOD(HttpRequestPool
, next
)
464 getObject(http_requestpool_object
, obj
);
465 ++(obj
->iterator
.pos
);
470 /* {{{ proto void HttpRequestPool::rewind()
472 * Implements Iterator::rewind().
474 PHP_METHOD(HttpRequestPool
, rewind
)
477 getObject(http_requestpool_object
, obj
);
478 obj
->iterator
.pos
= 0;
483 /* {{{ proto int HttpRequestPool::count()
485 * Implements Countable.
487 * Returns the number of attached HttpRequest objects.
489 PHP_METHOD(HttpRequestPool
, count
)
492 getObject(http_requestpool_object
, obj
);
493 RETURN_LONG((long) zend_llist_count(&obj
->pool
.handles
));
498 /* {{{ proto array HttpRequestPool::getAttachedRequests()
500 * Get attached HttpRequest objects.
502 * Returns an array containing all currently attached HttpRequest objects.
504 PHP_METHOD(HttpRequestPool
, getAttachedRequests
)
506 getObject(http_requestpool_object
, obj
);
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
);
517 /* {{{ proto array HttpRequestPool::getFinishedRequests()
519 * Get attached HttpRequest objects that already have finished their work.
521 * Returns an array containing all attached HttpRequest objects that
522 * already have finished their work.
524 PHP_METHOD(HttpRequestPool
, getFinishedRequests
)
526 getObject(http_requestpool_object
, obj
);
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
);
537 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
544 * vim600: noet sw=4 ts=4 fdm=marker
545 * vim<600: noet sw=4 ts=4