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-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
14 #include "php_http_client.h"
16 #include <ext/spl/spl_observer.h>
19 * array of name => php_http_client_driver_t*
21 static HashTable php_http_client_drivers
;
23 static void php_http_client_driver_hash_dtor(zval
*pData
)
25 efree(Z_PTR_P(pData
));
28 ZEND_RESULT_CODE
php_http_client_driver_add(php_http_client_driver_t
*driver
)
30 return zend_hash_str_add_mem(&php_http_client_drivers
, driver
->name_str
, driver
->name_len
, (void *) driver
, sizeof(php_http_client_driver_t
))
34 php_http_client_driver_t
*php_http_client_driver_get(const char *name_str
, size_t name_len
)
37 php_http_client_driver_t
*tmp
;
39 if (name_str
&& (tmp
= zend_hash_str_find_ptr(&php_http_client_drivers
, name_str
, name_len
))) {
42 if ((ztmp
= zend_hash_get_current_data(&php_http_client_drivers
))) {
48 static int apply_driver_list(zval
*p
, void *arg
)
50 php_http_client_driver_t
*d
= Z_PTR_P(p
);
53 ZVAL_STRINGL(&zname
, d
->name_str
, d
->name_len
);
55 zend_hash_next_index_insert(arg
, &zname
);
56 return ZEND_HASH_APPLY_KEEP
;
59 void php_http_client_driver_list(HashTable
*ht
)
61 zend_hash_apply_with_argument(&php_http_client_drivers
, apply_driver_list
, ht
);
64 void php_http_client_options_set_subr(zval
*instance
, char *key
, size_t len
, zval
*opts
, int overwrite
)
66 if (overwrite
|| (opts
&& zend_hash_num_elements(Z_ARRVAL_P(opts
)))) {
67 zend_class_entry
*this_ce
= Z_OBJCE_P(instance
);
68 zval
*old_opts
, new_opts
, *entry
= NULL
;
70 array_init(&new_opts
);
71 old_opts
= zend_read_property(this_ce
, instance
, ZEND_STRL("options"), 0);
72 if (Z_TYPE_P(old_opts
) == IS_ARRAY
) {
73 array_copy(Z_ARRVAL_P(old_opts
), Z_ARRVAL(new_opts
));
77 if (opts
&& zend_hash_num_elements(Z_ARRVAL_P(opts
))) {
79 zend_symtable_str_update(Z_ARRVAL(new_opts
), key
, len
, opts
);
81 zend_symtable_str_del(Z_ARRVAL(new_opts
), key
, len
);
83 } else if (opts
&& zend_hash_num_elements(Z_ARRVAL_P(opts
))) {
84 if ((entry
= zend_symtable_str_find(Z_ARRVAL(new_opts
), key
, len
))) {
85 array_join(Z_ARRVAL_P(opts
), Z_ARRVAL_P(entry
), 0, 0);
88 zend_symtable_str_update(Z_ARRVAL(new_opts
), key
, len
, opts
);
92 zend_update_property(this_ce
, instance
, ZEND_STRL("options"), &new_opts
);
93 zval_ptr_dtor(&new_opts
);
97 void php_http_client_options_set(zval
*instance
, zval
*opts
)
99 php_http_arrkey_t key
;
101 zend_class_entry
*this_ce
= Z_OBJCE_P(instance
);
102 zend_bool is_client
= instanceof_function(this_ce
, php_http_client_class_entry
);
104 array_init(&new_opts
);
106 if (!opts
|| !zend_hash_num_elements(Z_ARRVAL_P(opts
))) {
107 zend_update_property(this_ce
, instance
, ZEND_STRL("options"), &new_opts
);
108 zval_ptr_dtor(&new_opts
);
110 zval
*old_opts
, add_opts
, *opt
;
112 array_init(&add_opts
);
113 /* some options need extra attention -- thus cannot use array_merge() directly */
114 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(opts
), key
.h
, key
.key
, opt
)
117 if (Z_TYPE_P(opt
) == IS_ARRAY
&& (zend_string_equals_literal(key
.key
, "ssl") || zend_string_equals_literal(key
.key
, "cookies"))) {
118 php_http_client_options_set_subr(instance
, key
.key
->val
, key
.key
->len
, opt
, 0);
119 } else if (is_client
&& (zend_string_equals_literal(key
.key
, "recordHistory") || zend_string_equals_literal(key
.key
, "responseMessageClass"))) {
120 zend_update_property(this_ce
, instance
, key
.key
->val
, key
.key
->len
, opt
);
121 } else if (Z_TYPE_P(opt
) == IS_NULL
) {
122 old_opts
= zend_read_property(this_ce
, instance
, ZEND_STRL("options"), 0);
123 if (Z_TYPE_P(old_opts
) == IS_ARRAY
) {
124 zend_symtable_del(Z_ARRVAL_P(old_opts
), key
.key
);
128 add_assoc_zval_ex(&add_opts
, key
.key
->val
, key
.key
->len
, opt
);
132 ZEND_HASH_FOREACH_END();
134 old_opts
= zend_read_property(this_ce
, instance
, ZEND_STRL("options"), 0);
135 if (Z_TYPE_P(old_opts
) == IS_ARRAY
) {
136 array_copy(Z_ARRVAL_P(old_opts
), Z_ARRVAL(new_opts
));
138 array_join(Z_ARRVAL(add_opts
), Z_ARRVAL(new_opts
), 0, 0);
139 zend_update_property(this_ce
, instance
, ZEND_STRL("options"), &new_opts
);
140 zval_ptr_dtor(&new_opts
);
141 zval_ptr_dtor(&add_opts
);
145 void php_http_client_options_get_subr(zval
*instance
, char *key
, size_t len
, zval
*return_value
)
147 zend_class_entry
*this_ce
= Z_OBJCE_P(instance
);
148 zval
*options
, *opts
= zend_read_property(this_ce
, instance
, ZEND_STRL("options"), 0);
150 if ((Z_TYPE_P(opts
) == IS_ARRAY
) && (options
= zend_symtable_str_find(Z_ARRVAL_P(opts
), key
, len
))) {
151 RETVAL_ZVAL_FAST(options
);
155 static void queue_dtor(void *enqueued
)
157 php_http_client_enqueue_t
*e
= enqueued
;
164 php_http_client_t
*php_http_client_init(php_http_client_t
*h
, php_http_client_ops_t
*ops
, php_resource_factory_t
*rf
, void *init_arg
)
166 php_http_client_t
*free_h
= NULL
;
169 free_h
= h
= emalloc(sizeof(*h
));
171 memset(h
, 0, sizeof(*h
));
176 } else if (ops
->rsrc
) {
177 h
->rf
= php_resource_factory_init(NULL
, h
->ops
->rsrc
, h
, NULL
);
179 zend_llist_init(&h
->requests
, sizeof(php_http_client_enqueue_t
), queue_dtor
, 0);
180 zend_llist_init(&h
->responses
, sizeof(void *), NULL
, 0);
183 if (!(h
= h
->ops
->init(h
, init_arg
))) {
184 php_error_docref(NULL
, E_WARNING
, "Could not initialize client");
192 php_http_client_t
*php_http_client_copy(php_http_client_t
*from
, php_http_client_t
*to
)
194 if (from
->ops
->copy
) {
195 return from
->ops
->copy(from
, to
);
201 void php_http_client_dtor(php_http_client_t
*h
)
203 php_http_client_reset(h
);
209 php_resource_factory_free(&h
->rf
);
212 void php_http_client_free(php_http_client_t
**h
) {
214 php_http_client_dtor(*h
);
220 ZEND_RESULT_CODE
php_http_client_enqueue(php_http_client_t
*h
, php_http_client_enqueue_t
*enqueue
)
222 if (h
->ops
->enqueue
) {
223 if (php_http_client_enqueued(h
, enqueue
->request
, NULL
)) {
224 php_error_docref(NULL
, E_WARNING
, "Failed to enqueue request; request already in queue");
227 return h
->ops
->enqueue(h
, enqueue
);
233 ZEND_RESULT_CODE
php_http_client_dequeue(php_http_client_t
*h
, php_http_message_t
*request
)
235 if (h
->ops
->dequeue
) {
236 php_http_client_enqueue_t
*enqueue
= php_http_client_enqueued(h
, request
, NULL
);
239 php_error_docref(NULL
, E_WARNING
, "Failed to dequeue request; request not in queue");
242 return h
->ops
->dequeue(h
, enqueue
);
247 php_http_client_enqueue_t
*php_http_client_enqueued(php_http_client_t
*h
, void *compare_arg
, php_http_client_enqueue_cmp_func_t compare_func
)
249 zend_llist_element
*el
= NULL
;
252 for (el
= h
->requests
.head
; el
; el
= el
->next
) {
253 if (compare_func((php_http_client_enqueue_t
*) el
->data
, compare_arg
)) {
258 for (el
= h
->requests
.head
; el
; el
= el
->next
) {
259 if (((php_http_client_enqueue_t
*) el
->data
)->request
== compare_arg
) {
264 return el
? (php_http_client_enqueue_t
*) el
->data
: NULL
;
267 ZEND_RESULT_CODE
php_http_client_wait(php_http_client_t
*h
, struct timeval
*custom_timeout
)
270 return h
->ops
->wait(h
, custom_timeout
);
276 int php_http_client_once(php_http_client_t
*h
)
279 return h
->ops
->once(h
);
285 ZEND_RESULT_CODE
php_http_client_exec(php_http_client_t
*h
)
288 return h
->ops
->exec(h
);
294 void php_http_client_reset(php_http_client_t
*h
)
300 zend_llist_clean(&h
->requests
);
301 zend_llist_clean(&h
->responses
);
304 ZEND_RESULT_CODE
php_http_client_setopt(php_http_client_t
*h
, php_http_client_setopt_opt_t opt
, void *arg
)
306 if (h
->ops
->setopt
) {
307 return h
->ops
->setopt(h
, opt
, arg
);
313 ZEND_RESULT_CODE
php_http_client_getopt(php_http_client_t
*h
, php_http_client_getopt_opt_t opt
, void *arg
, void *res_ptr
)
315 if (h
->ops
->getopt
) {
316 return h
->ops
->getopt(h
, opt
, arg
, res_ptr
);
321 zend_class_entry
*php_http_client_class_entry
;
322 static zend_object_handlers php_http_client_object_handlers
;
324 void php_http_client_object_free(zend_object
*object
)
326 php_http_client_object_t
*o
= PHP_HTTP_OBJ(object
, NULL
);
328 php_http_client_free(&o
->client
);
329 zend_object_std_dtor(object
);
332 php_http_client_object_t
*php_http_client_object_new_ex(zend_class_entry
*ce
, php_http_client_t
*client
)
334 php_http_client_object_t
*o
;
336 o
= ecalloc(1, sizeof(php_http_client_object_t
) + (ce
->default_properties_count
- 1) * sizeof(zval
));
337 zend_object_std_init(&o
->zo
, ce
);
338 object_properties_init(&o
->zo
, ce
);
342 o
->zo
.handlers
= &php_http_client_object_handlers
;
347 zend_object
*php_http_client_object_new(zend_class_entry
*ce
)
349 return &php_http_client_object_new_ex(ce
, NULL
)->zo
;
352 static void handle_history(zval
*zclient
, php_http_message_t
*request
, php_http_message_t
*response
)
354 zval new_hist
, *old_hist
= zend_read_property(php_http_client_class_entry
, zclient
, ZEND_STRL("history"), 0);
355 php_http_message_t
*req_copy
= php_http_message_copy(request
, NULL
);
356 php_http_message_t
*res_copy
= php_http_message_copy(response
, NULL
);
357 php_http_message_t
*zipped
= php_http_message_zip(res_copy
, req_copy
);
358 php_http_message_object_t
*obj
= php_http_message_object_new_ex(php_http_message_class_entry
, zipped
);
360 ZVAL_OBJ(&new_hist
, &obj
->zo
);
362 if (Z_TYPE_P(old_hist
) == IS_OBJECT
) {
363 php_http_message_object_prepend(&new_hist
, old_hist
, 1);
366 zend_update_property(php_http_client_class_entry
, zclient
, ZEND_STRL("history"), &new_hist
);
367 zval_ptr_dtor(&new_hist
);
370 static ZEND_RESULT_CODE
handle_response(void *arg
, php_http_client_t
*client
, php_http_client_enqueue_t
*e
, php_http_message_t
**request
, php_http_message_t
**response
)
372 zend_bool dequeue
= 0;
374 php_http_message_t
*msg
;
375 php_http_client_progress_state_t
*progress
;
377 ZVAL_OBJ(&zclient
, &((php_http_client_object_t
*) arg
)->zo
);
379 if ((msg
= *response
)) {
380 php_http_message_object_t
*msg_obj
;
381 zval info
, zresponse
, zrequest
;
384 /* ensure the message is of type response (could be uninitialized in case of early error, like DNS) */
385 php_http_message_set_type(msg
, PHP_HTTP_RESPONSE
);
387 if (zend_is_true(zend_read_property(php_http_client_class_entry
, &zclient
, ZEND_STRL("recordHistory"), 0))) {
388 handle_history(&zclient
, *request
, *response
);
391 /* hard detach, redirects etc. are in the history */
392 php_http_message_free(&msg
->parent
);
395 msg_obj
= php_http_message_object_new_ex(php_http_client_response_class_entry
, msg
);
396 ZVAL_OBJ(&zresponse
, &msg_obj
->zo
);
397 ZVAL_OBJECT(&zrequest
, &((php_http_message_object_t
*) e
->opaque
)->zo
, 1);
399 php_http_message_object_prepend(&zresponse
, &zrequest
, 1);
402 info_ht
= HASH_OF(&info
);
403 php_http_client_getopt(client
, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO
, e
->request
, &info_ht
);
404 zend_update_property(php_http_client_response_class_entry
, &zresponse
, ZEND_STRL("transferInfo"), &info
);
405 zval_ptr_dtor(&info
);
408 zend_llist_add_element(&client
->responses
, &msg_obj
);
410 if (e
->closure
.fci
.size
) {
412 zend_error_handling zeh
;
415 zend_fcall_info_argn(&e
->closure
.fci
, 1, &zresponse
);
416 zend_replace_error_handling(EH_NORMAL
, NULL
, &zeh
);
417 zend_fcall_info_call(&e
->closure
.fci
, &e
->closure
.fcc
, &retval
, NULL
);
418 zend_restore_error_handling(&zeh
);
419 zend_fcall_info_argn(&e
->closure
.fci
, 0);
421 if (Z_TYPE(retval
) == IS_TRUE
) {
424 zval_ptr_dtor(&retval
);
427 zval_ptr_dtor(&zresponse
);
428 zval_ptr_dtor(&zrequest
);
431 if (SUCCESS
== php_http_client_getopt(client
, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO
, e
->request
, &progress
)) {
432 progress
->info
= "finished";
433 progress
->finished
= 1;
434 client
->callback
.progress
.func(client
->callback
.progress
.arg
, client
, e
, progress
);
438 php_http_client_dequeue(client
, e
->request
);
444 static void handle_progress(void *arg
, php_http_client_t
*client
, php_http_client_enqueue_t
*e
, php_http_client_progress_state_t
*progress
)
446 zval zrequest
, zprogress
, retval
, zclient
;
447 zend_error_handling zeh
;
450 ZVAL_OBJECT(&zclient
, &((php_http_client_object_t
*) arg
)->zo
, 1);
451 ZVAL_OBJECT(&zrequest
, &((php_http_message_object_t
*) e
->opaque
)->zo
, 1);
452 object_init(&zprogress
);
453 add_property_bool(&zprogress
, "started", progress
->started
);
454 add_property_bool(&zprogress
, "finished", progress
->finished
);
455 add_property_string(&zprogress
, "info", STR_PTR(progress
->info
));
456 add_property_double(&zprogress
, "dltotal", progress
->dl
.total
);
457 add_property_double(&zprogress
, "dlnow", progress
->dl
.now
);
458 add_property_double(&zprogress
, "ultotal", progress
->ul
.total
);
459 add_property_double(&zprogress
, "ulnow", progress
->ul
.now
);
460 zend_replace_error_handling(EH_NORMAL
, NULL
, &zeh
);
461 zend_call_method_with_2_params(&zclient
, NULL
, NULL
, "notify", &retval
, &zrequest
, &zprogress
);
462 zend_restore_error_handling(&zeh
);
463 zval_ptr_dtor(&zclient
);
464 zval_ptr_dtor(&zrequest
);
465 zval_ptr_dtor(&zprogress
);
466 zval_ptr_dtor(&retval
);
469 static void response_dtor(void *data
)
471 php_http_message_object_t
*msg_obj
= *(php_http_message_object_t
**) data
;
473 zend_objects_store_del(&msg_obj
->zo
);
476 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_construct
, 0, 0, 0)
477 ZEND_ARG_INFO(0, driver
)
478 ZEND_ARG_INFO(0, persistent_handle_id
)
480 static PHP_METHOD(HttpClient
, __construct
)
482 char *driver_str
= NULL
, *persistent_handle_str
= NULL
;
483 size_t driver_len
= 0, persistent_handle_len
= 0;
484 php_http_client_driver_t
*driver
;
485 php_resource_factory_t
*rf
= NULL
;
486 php_http_client_object_t
*obj
;
489 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!", &driver_str
, &driver_len
, &persistent_handle_str
, &persistent_handle_len
), invalid_arg
, return);
491 if (!zend_hash_num_elements(&php_http_client_drivers
)) {
492 php_http_throw(unexpected_val
, "No http\\Client drivers available", NULL
);
495 if (!(driver
= php_http_client_driver_get(driver_str
, driver_len
))) {
496 php_http_throw(unexpected_val
, "Failed to locate \"%s\" client request handler", driver_len
? driver_str
: "default");
500 object_init_ex(&os
, spl_ce_SplObjectStorage
);
501 zend_update_property(php_http_client_class_entry
, getThis(), ZEND_STRL("observers"), &os
);
504 if (persistent_handle_len
) {
507 php_persistent_handle_factory_t
*pf
;
509 name_len
= spprintf(&name_str
, 0, "http\\Client\\%s", driver
->name_str
);
510 php_http_pretty_key(name_str
+ lenof("http\\Client\\"), driver
->name_len
, 1, 1);
512 if ((pf
= php_persistent_handle_concede(NULL
, name_str
, name_len
, persistent_handle_str
, persistent_handle_len
, NULL
, NULL
))) {
513 rf
= php_resource_factory_init(NULL
, php_persistent_handle_get_resource_factory_ops(), pf
, (void (*)(void *)) php_persistent_handle_abandon
);
519 obj
= PHP_HTTP_OBJ(NULL
, getThis());
521 php_http_expect(obj
->client
= php_http_client_init(NULL
, driver
->client_ops
, rf
, NULL
), runtime
, return);
523 obj
->client
->callback
.response
.func
= handle_response
;
524 obj
->client
->callback
.response
.arg
= obj
;
525 obj
->client
->callback
.progress
.func
= handle_progress
;
526 obj
->client
->callback
.progress
.arg
= obj
;
528 obj
->client
->responses
.dtor
= response_dtor
;
531 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_reset
, 0, 0, 0)
533 static PHP_METHOD(HttpClient
, reset
)
535 php_http_client_object_t
*obj
;
536 php_http_expect(SUCCESS
== zend_parse_parameters_none(), invalid_arg
, return);
538 obj
= PHP_HTTP_OBJ(NULL
, getThis());
541 php_http_client_reset(obj
->client
);
543 RETVAL_ZVAL_FAST(getThis());
546 static HashTable
*combined_options(zval
*client
, zval
*request
)
549 unsigned num_options
= 0;
550 zval z_roptions
, *z_coptions
= zend_read_property(php_http_client_class_entry
, client
, ZEND_STRL("options"), 0);
552 if (Z_TYPE_P(z_coptions
) == IS_ARRAY
) {
553 num_options
= zend_hash_num_elements(Z_ARRVAL_P(z_coptions
));
555 ZVAL_UNDEF(&z_roptions
);
556 zend_call_method_with_0_params(request
, NULL
, NULL
, "getOptions", &z_roptions
);
557 if (Z_TYPE(z_roptions
) == IS_ARRAY
) {
558 unsigned num
= zend_hash_num_elements(Z_ARRVAL(z_roptions
));
559 if (num
> num_options
) {
563 ALLOC_HASHTABLE(options
);
564 ZEND_INIT_SYMTABLE_EX(options
, num_options
, 0);
565 if (Z_TYPE_P(z_coptions
) == IS_ARRAY
) {
566 array_copy(Z_ARRVAL_P(z_coptions
), options
);
568 if (Z_TYPE(z_roptions
) == IS_ARRAY
) {
569 array_join(Z_ARRVAL(z_roptions
), options
, 0, 0);
571 zval_ptr_dtor(&z_roptions
);
576 static void msg_queue_dtor(php_http_client_enqueue_t
*e
)
578 php_http_message_object_t
*msg_obj
= e
->opaque
;
580 zend_objects_store_del(&msg_obj
->zo
);
581 zend_hash_destroy(e
->options
);
582 FREE_HASHTABLE(e
->options
);
584 if (e
->closure
.fci
.size
) {
585 zval_ptr_dtor(&e
->closure
.fci
.function_name
);
586 if (e
->closure
.fci
.object
) {
587 zend_objects_store_del(e
->closure
.fci
.object
);
592 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enqueue
, 0, 0, 1)
593 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 0)
594 ZEND_ARG_INFO(0, callable
)
596 static PHP_METHOD(HttpClient
, enqueue
)
599 zend_fcall_info fci
= empty_fcall_info
;
600 zend_fcall_info_cache fcc
= empty_fcall_info_cache
;
601 php_http_client_object_t
*obj
;
602 php_http_message_object_t
*msg_obj
;
603 php_http_client_enqueue_t q
;
605 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O|f", &request
, php_http_client_request_class_entry
, &fci
, &fcc
), invalid_arg
, return);
607 obj
= PHP_HTTP_OBJ(NULL
, getThis());
608 msg_obj
= PHP_HTTP_OBJ(NULL
, request
);
610 if (php_http_client_enqueued(obj
->client
, msg_obj
->message
, NULL
)) {
611 php_http_throw(bad_method_call
, "Failed to enqueue request; request already in queue", NULL
);
615 q
.request
= msg_obj
->message
;
616 q
.options
= combined_options(getThis(), request
);
617 q
.dtor
= msg_queue_dtor
;
623 Z_TRY_ADDREF(fci
.function_name
);
625 ++GC_REFCOUNT(fci
.object
);
631 php_http_expect(SUCCESS
== php_http_client_enqueue(obj
->client
, &q
), runtime
,
636 RETVAL_ZVAL_FAST(getThis());
639 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_dequeue
, 0, 0, 1)
640 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 0)
642 static PHP_METHOD(HttpClient
, dequeue
)
645 php_http_client_object_t
*obj
;
646 php_http_message_object_t
*msg_obj
;
648 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request
, php_http_client_request_class_entry
), invalid_arg
, return);
650 obj
= PHP_HTTP_OBJ(NULL
, getThis());
651 msg_obj
= PHP_HTTP_OBJ(NULL
, request
);
653 if (!php_http_client_enqueued(obj
->client
, msg_obj
->message
, NULL
)) {
654 php_http_throw(bad_method_call
, "Failed to dequeue request; request not in queue", NULL
);
658 php_http_expect(SUCCESS
== php_http_client_dequeue(obj
->client
, msg_obj
->message
), runtime
, return);
660 RETVAL_ZVAL_FAST(getThis());
663 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_requeue
, 0, 0, 1)
664 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 0)
665 ZEND_ARG_INFO(0, callable
)
667 static PHP_METHOD(HttpClient
, requeue
)
670 zend_fcall_info fci
= empty_fcall_info
;
671 zend_fcall_info_cache fcc
= empty_fcall_info_cache
;
672 php_http_client_object_t
*obj
;
673 php_http_message_object_t
*msg_obj
;
674 php_http_client_enqueue_t q
;
676 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O|f", &request
, php_http_client_request_class_entry
, &fci
, &fcc
), invalid_arg
, return);
678 obj
= PHP_HTTP_OBJ(NULL
, getThis());
679 msg_obj
= PHP_HTTP_OBJ(NULL
, request
);
681 if (php_http_client_enqueued(obj
->client
, msg_obj
->message
, NULL
)) {
682 php_http_expect(SUCCESS
== php_http_client_dequeue(obj
->client
, msg_obj
->message
), runtime
, return);
685 q
.request
= msg_obj
->message
;
686 q
.options
= combined_options(getThis(), request
);
687 q
.dtor
= msg_queue_dtor
;
693 Z_TRY_ADDREF(fci
.function_name
);
695 ++GC_REFCOUNT(fci
.object
);
701 php_http_expect(SUCCESS
== php_http_client_enqueue(obj
->client
, &q
), runtime
,
706 RETVAL_ZVAL_FAST(getThis());
709 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_count
, 0, 0, 0)
711 static PHP_METHOD(HttpClient
, count
)
713 zend_long count_mode
= -1;
715 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &count_mode
)) {
716 php_http_client_object_t
*obj
= PHP_HTTP_OBJ(NULL
, getThis());
718 RETVAL_LONG(zend_llist_count(&obj
->client
->requests
));
722 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getResponse
, 0, 0, 0)
723 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 1)
725 static PHP_METHOD(HttpClient
, getResponse
)
727 zval
*zrequest
= NULL
;
728 php_http_client_object_t
*obj
;
730 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|O", &zrequest
, php_http_client_request_class_entry
), invalid_arg
, return);
732 obj
= PHP_HTTP_OBJ(NULL
, getThis());
735 /* lookup the response with the request */
736 zend_llist_element
*el
= NULL
;
737 php_http_message_object_t
*req_obj
= PHP_HTTP_OBJ(NULL
, zrequest
);
739 for (el
= obj
->client
->responses
.head
; el
; el
= el
->next
) {
740 php_http_message_object_t
*response_obj
= *(php_http_message_object_t
**) el
->data
;
742 if (response_obj
->message
->parent
== req_obj
->message
) {
743 RETURN_OBJECT(&response_obj
->zo
, 1);
747 /* not found for the request! */
748 php_http_throw(unexpected_val
, "Could not find response for the request", NULL
);
752 /* pop off the last response */
753 if (obj
->client
->responses
.tail
) {
754 php_http_message_object_t
*response_obj
= *(php_http_message_object_t
**) obj
->client
->responses
.tail
->data
;
758 RETVAL_OBJECT(&response_obj
->zo
, 1);
759 zend_llist_remove_tail(&obj
->client
->responses
);
764 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getHistory
, 0, 0, 0)
766 static PHP_METHOD(HttpClient
, getHistory
)
770 php_http_expect(SUCCESS
== zend_parse_parameters_none(), invalid_arg
, return);
772 zhistory
= zend_read_property(php_http_client_class_entry
, getThis(), ZEND_STRL("history"), 0);
773 RETVAL_ZVAL_FAST(zhistory
);
776 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_send
, 0, 0, 0)
778 static PHP_METHOD(HttpClient
, send
)
780 php_http_client_object_t
*obj
;
782 php_http_expect(SUCCESS
== zend_parse_parameters_none(), invalid_arg
, return);
784 obj
= PHP_HTTP_OBJ(NULL
, getThis());
786 php_http_expect(SUCCESS
== php_http_client_exec(obj
->client
), runtime
, return);
788 RETVAL_ZVAL_FAST(getThis());
791 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_once
, 0, 0, 0)
793 static PHP_METHOD(HttpClient
, once
)
795 if (SUCCESS
== zend_parse_parameters_none()) {
796 php_http_client_object_t
*obj
= PHP_HTTP_OBJ(NULL
, getThis());
798 RETURN_BOOL(0 < php_http_client_once(obj
->client
));
802 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_wait
, 0, 0, 0)
803 ZEND_ARG_INFO(0, timeout
)
805 static PHP_METHOD(HttpClient
, wait
)
809 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|d", &timeout
)) {
810 struct timeval timeout_val
;
811 php_http_client_object_t
*obj
= PHP_HTTP_OBJ(NULL
, getThis());
813 timeout_val
.tv_sec
= (time_t) timeout
;
814 timeout_val
.tv_usec
= PHP_HTTP_USEC(timeout
) % PHP_HTTP_MCROSEC
;
816 RETURN_BOOL(SUCCESS
== php_http_client_wait(obj
->client
, timeout
> 0 ? &timeout_val
: NULL
));
820 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enablePipelining
, 0, 0, 0)
821 ZEND_ARG_INFO(0, enable
)
823 static PHP_METHOD(HttpClient
, enablePipelining
)
825 zend_bool enable
= 1;
826 php_http_client_object_t
*obj
;
828 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &enable
), invalid_arg
, return);
830 obj
= PHP_HTTP_OBJ(NULL
, getThis());
832 php_http_expect(SUCCESS
== php_http_client_setopt(obj
->client
, PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING
, &enable
), unexpected_val
, return);
834 RETVAL_ZVAL_FAST(getThis());
837 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enableEvents
, 0, 0, 0)
838 ZEND_ARG_INFO(0, enable
)
840 static PHP_METHOD(HttpClient
, enableEvents
)
842 zend_bool enable
= 1;
843 php_http_client_object_t
*obj
;
845 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &enable
), invalid_arg
, return);
847 obj
= PHP_HTTP_OBJ(NULL
, getThis());
849 php_http_expect(SUCCESS
== php_http_client_setopt(obj
->client
, PHP_HTTP_CLIENT_OPT_USE_EVENTS
, &enable
), unexpected_val
, return);
851 RETVAL_ZVAL_FAST(getThis());
854 static int notify(zend_object_iterator
*iter
, void *puser
)
856 zval
*observer
, *args
= puser
;
858 if ((observer
= iter
->funcs
->get_current_data(iter
))) {
859 int num_args
= !Z_ISUNDEF(args
[0]) + !Z_ISUNDEF(args
[1]) + !Z_ISUNDEF(args
[2]);
860 return php_http_method_call(observer
, ZEND_STRL("update"), num_args
, args
, NULL
);
865 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_notify
, 0, 0, 0)
866 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 1)
868 static PHP_METHOD(HttpClient
, notify
)
870 zval
*request
= NULL
, *zprogress
= NULL
, *observers
, args
[3];
872 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|O!o!", &request
, php_http_client_request_class_entry
, &zprogress
), invalid_arg
, return);
874 observers
= zend_read_property(php_http_client_class_entry
, getThis(), ZEND_STRL("observers"), 0);
876 if (Z_TYPE_P(observers
) != IS_OBJECT
) {
877 php_http_throw(unexpected_val
, "Observer storage is corrupted", NULL
);
881 ZVAL_COPY(&args
[0], getThis());
883 ZVAL_COPY(&args
[1], request
);
885 ZVAL_UNDEF(&args
[1]);
888 ZVAL_COPY(&args
[2], zprogress
);
890 ZVAL_UNDEF(&args
[2]);
893 spl_iterator_apply(observers
, notify
, args
);
895 zval_ptr_dtor(getThis());
897 zval_ptr_dtor(request
);
900 zval_ptr_dtor(zprogress
);
903 RETVAL_ZVAL_FAST(getThis());
906 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_attach
, 0, 0, 1)
907 ZEND_ARG_OBJ_INFO(0, observer
, SplObserver
, 0)
909 static PHP_METHOD(HttpClient
, attach
)
911 zval
*observers
, *observer
, retval
;
913 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O", &observer
, spl_ce_SplObserver
), invalid_arg
, return);
915 observers
= zend_read_property(php_http_client_class_entry
, getThis(), ZEND_STRL("observers"), 0);
917 if (Z_TYPE_P(observers
) != IS_OBJECT
) {
918 php_http_throw(unexpected_val
, "Observer storage is corrupted", NULL
);
923 zend_call_method_with_1_params(observers
, NULL
, NULL
, "attach", &retval
, observer
);
924 zval_ptr_dtor(&retval
);
926 RETVAL_ZVAL_FAST(getThis());
929 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_detach
, 0, 0, 1)
930 ZEND_ARG_OBJ_INFO(0, observer
, SplObserver
, 0)
932 static PHP_METHOD(HttpClient
, detach
)
934 zval
*observers
, *observer
, retval
;
936 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O", &observer
, spl_ce_SplObserver
), invalid_arg
, return);
938 observers
= zend_read_property(php_http_client_class_entry
, getThis(), ZEND_STRL("observers"), 0);
940 if (Z_TYPE_P(observers
) != IS_OBJECT
) {
941 php_http_throw(unexpected_val
, "Observer storage is corrupted", NULL
);
946 zend_call_method_with_1_params(observers
, NULL
, NULL
, "detach", &retval
, observer
);
947 zval_ptr_dtor(&retval
);
949 RETVAL_ZVAL_FAST(getThis());
952 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getObservers
, 0, 0, 0)
954 static PHP_METHOD(HttpClient
, getObservers
)
958 php_http_expect(SUCCESS
== zend_parse_parameters_none(), invalid_arg
, return);
960 observers
= zend_read_property(php_http_client_class_entry
, getThis(), ZEND_STRL("observers"), 0);
962 if (Z_TYPE_P(observers
) != IS_OBJECT
) {
963 php_http_throw(unexpected_val
, "Observer storage is corrupted", NULL
);
967 RETVAL_ZVAL_FAST(observers
);
970 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getProgressInfo
, 0, 0, 1)
971 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 0)
973 static PHP_METHOD(HttpClient
, getProgressInfo
)
976 php_http_client_object_t
*obj
;
977 php_http_message_object_t
*req_obj
;
978 php_http_client_progress_state_t
*progress
;
980 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request
, php_http_client_request_class_entry
), invalid_arg
, return);
982 obj
= PHP_HTTP_OBJ(NULL
, getThis());
983 req_obj
= PHP_HTTP_OBJ(NULL
, request
);
985 php_http_expect(SUCCESS
== php_http_client_getopt(obj
->client
, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO
, req_obj
->message
, &progress
), unexpected_val
, return);
987 object_init(return_value
);
988 add_property_bool(return_value
, "started", progress
->started
);
989 add_property_bool(return_value
, "finished", progress
->finished
);
990 add_property_string(return_value
, "info", STR_PTR(progress
->info
));
991 add_property_double(return_value
, "dltotal", progress
->dl
.total
);
992 add_property_double(return_value
, "dlnow", progress
->dl
.now
);
993 add_property_double(return_value
, "ultotal", progress
->ul
.total
);
994 add_property_double(return_value
, "ulnow", progress
->ul
.now
);
997 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getTransferInfo
, 0, 0, 1)
998 ZEND_ARG_OBJ_INFO(0, request
, http
\\Client
\\Request
, 0)
1000 static PHP_METHOD(HttpClient
, getTransferInfo
)
1004 php_http_client_object_t
*obj
;
1005 php_http_message_object_t
*req_obj
;
1007 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request
, php_http_client_request_class_entry
), invalid_arg
, return);
1009 obj
= PHP_HTTP_OBJ(NULL
, getThis());
1010 req_obj
= PHP_HTTP_OBJ(NULL
, request
);
1012 object_init(return_value
);
1013 info
= HASH_OF(return_value
);
1014 php_http_expect(SUCCESS
== php_http_client_getopt(obj
->client
, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO
, req_obj
->message
, &info
), unexpected_val
, return);
1017 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setOptions
, 0, 0, 0)
1018 ZEND_ARG_ARRAY_INFO(0, options
, 1)
1019 ZEND_END_ARG_INFO();
1020 static PHP_METHOD(HttpClient
, setOptions
)
1024 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts
), invalid_arg
, return);
1026 php_http_client_options_set(getThis(), opts
);
1028 RETVAL_ZVAL_FAST(getThis());
1031 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getOptions
, 0, 0, 0)
1032 ZEND_END_ARG_INFO();
1033 static PHP_METHOD(HttpClient
, getOptions
)
1035 if (SUCCESS
== zend_parse_parameters_none()) {
1036 zval
*options
= zend_read_property(php_http_client_class_entry
, getThis(), ZEND_STRL("options"), 0);
1037 RETVAL_ZVAL_FAST(options
);
1041 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setSslOptions
, 0, 0, 0)
1042 ZEND_ARG_ARRAY_INFO(0, ssl_option
, 1)
1043 ZEND_END_ARG_INFO();
1044 static PHP_METHOD(HttpClient
, setSslOptions
)
1048 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts
), invalid_arg
, return);
1050 php_http_client_options_set_subr(getThis(), ZEND_STRL("ssl"), opts
, 1);
1052 RETVAL_ZVAL_FAST(getThis());
1055 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addSslOptions
, 0, 0, 0)
1056 ZEND_ARG_ARRAY_INFO(0, ssl_options
, 1)
1057 ZEND_END_ARG_INFO();
1058 static PHP_METHOD(HttpClient
, addSslOptions
)
1062 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts
), invalid_arg
, return);
1064 php_http_client_options_set_subr(getThis(), ZEND_STRL("ssl"), opts
, 0);
1066 RETVAL_ZVAL_FAST(getThis());
1069 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getSslOptions
, 0, 0, 0)
1070 ZEND_END_ARG_INFO();
1071 static PHP_METHOD(HttpClient
, getSslOptions
)
1073 if (SUCCESS
== zend_parse_parameters_none()) {
1074 php_http_client_options_get_subr(getThis(), ZEND_STRL("ssl"), return_value
);
1078 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setCookies
, 0, 0, 0)
1079 ZEND_ARG_ARRAY_INFO(0, cookies
, 1)
1080 ZEND_END_ARG_INFO();
1081 static PHP_METHOD(HttpClient
, setCookies
)
1085 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts
), invalid_arg
, return);
1087 php_http_client_options_set_subr(getThis(), ZEND_STRL("cookies"), opts
, 1);
1089 RETVAL_ZVAL_FAST(getThis());
1092 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addCookies
, 0, 0, 0)
1093 ZEND_ARG_ARRAY_INFO(0, cookies
, 1)
1094 ZEND_END_ARG_INFO();
1095 static PHP_METHOD(HttpClient
, addCookies
)
1099 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts
), invalid_arg
, return);
1101 php_http_client_options_set_subr(getThis(), ZEND_STRL("cookies"), opts
, 0);
1103 RETVAL_ZVAL_FAST(getThis());
1106 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getCookies
, 0, 0, 0)
1107 ZEND_END_ARG_INFO();
1108 static PHP_METHOD(HttpClient
, getCookies
)
1110 if (SUCCESS
== zend_parse_parameters_none()) {
1111 php_http_client_options_get_subr(getThis(), ZEND_STRL("cookies"), return_value
);
1115 ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableDrivers
, 0, 0, 0)
1116 ZEND_END_ARG_INFO();
1117 static PHP_METHOD(HttpClient
, getAvailableDrivers
)
1119 if (SUCCESS
== zend_parse_parameters_none()) {
1120 array_init(return_value
);
1121 php_http_client_driver_list(Z_ARRVAL_P(return_value
));
1125 static zend_function_entry php_http_client_methods
[] = {
1126 PHP_ME(HttpClient
, __construct
, ai_HttpClient_construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
1127 PHP_ME(HttpClient
, reset
, ai_HttpClient_reset
, ZEND_ACC_PUBLIC
)
1128 PHP_ME(HttpClient
, enqueue
, ai_HttpClient_enqueue
, ZEND_ACC_PUBLIC
)
1129 PHP_ME(HttpClient
, dequeue
, ai_HttpClient_dequeue
, ZEND_ACC_PUBLIC
)
1130 PHP_ME(HttpClient
, requeue
, ai_HttpClient_requeue
, ZEND_ACC_PUBLIC
)
1131 PHP_ME(HttpClient
, count
, ai_HttpClient_count
, ZEND_ACC_PUBLIC
)
1132 PHP_ME(HttpClient
, send
, ai_HttpClient_send
, ZEND_ACC_PUBLIC
)
1133 PHP_ME(HttpClient
, once
, ai_HttpClient_once
, ZEND_ACC_PUBLIC
)
1134 PHP_ME(HttpClient
, wait
, ai_HttpClient_wait
, ZEND_ACC_PUBLIC
)
1135 PHP_ME(HttpClient
, getResponse
, ai_HttpClient_getResponse
, ZEND_ACC_PUBLIC
)
1136 PHP_ME(HttpClient
, getHistory
, ai_HttpClient_getHistory
, ZEND_ACC_PUBLIC
)
1137 PHP_ME(HttpClient
, enablePipelining
, ai_HttpClient_enablePipelining
, ZEND_ACC_PUBLIC
)
1138 PHP_ME(HttpClient
, enableEvents
, ai_HttpClient_enableEvents
, ZEND_ACC_PUBLIC
)
1139 PHP_ME(HttpClient
, notify
, ai_HttpClient_notify
, ZEND_ACC_PUBLIC
)
1140 PHP_ME(HttpClient
, attach
, ai_HttpClient_attach
, ZEND_ACC_PUBLIC
)
1141 PHP_ME(HttpClient
, detach
, ai_HttpClient_detach
, ZEND_ACC_PUBLIC
)
1142 PHP_ME(HttpClient
, getObservers
, ai_HttpClient_getObservers
, ZEND_ACC_PUBLIC
)
1143 PHP_ME(HttpClient
, getProgressInfo
, ai_HttpClient_getProgressInfo
, ZEND_ACC_PUBLIC
)
1144 PHP_ME(HttpClient
, getTransferInfo
, ai_HttpClient_getTransferInfo
, ZEND_ACC_PUBLIC
)
1145 PHP_ME(HttpClient
, setOptions
, ai_HttpClient_setOptions
, ZEND_ACC_PUBLIC
)
1146 PHP_ME(HttpClient
, getOptions
, ai_HttpClient_getOptions
, ZEND_ACC_PUBLIC
)
1147 PHP_ME(HttpClient
, setSslOptions
, ai_HttpClient_setSslOptions
, ZEND_ACC_PUBLIC
)
1148 PHP_ME(HttpClient
, addSslOptions
, ai_HttpClient_addSslOptions
, ZEND_ACC_PUBLIC
)
1149 PHP_ME(HttpClient
, getSslOptions
, ai_HttpClient_getSslOptions
, ZEND_ACC_PUBLIC
)
1150 PHP_ME(HttpClient
, setCookies
, ai_HttpClient_setCookies
, ZEND_ACC_PUBLIC
)
1151 PHP_ME(HttpClient
, addCookies
, ai_HttpClient_addCookies
, ZEND_ACC_PUBLIC
)
1152 PHP_ME(HttpClient
, getCookies
, ai_HttpClient_getCookies
, ZEND_ACC_PUBLIC
)
1153 PHP_ME(HttpClient
, getAvailableDrivers
, ai_HttpClient_getAvailableDrivers
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
1154 EMPTY_FUNCTION_ENTRY
1157 PHP_MINIT_FUNCTION(http_client
)
1159 zend_class_entry ce
= {0};
1161 INIT_NS_CLASS_ENTRY(ce
, "http", "Client", php_http_client_methods
);
1162 php_http_client_class_entry
= zend_register_internal_class_ex(&ce
, NULL
);
1163 php_http_client_class_entry
->create_object
= php_http_client_object_new
;
1164 zend_class_implements(php_http_client_class_entry
, 2, spl_ce_SplSubject
, spl_ce_Countable
);
1165 memcpy(&php_http_client_object_handlers
, zend_get_std_object_handlers(), sizeof(zend_object_handlers
));
1166 php_http_client_object_handlers
.offset
= XtOffsetOf(php_http_client_object_t
, zo
);
1167 php_http_client_object_handlers
.free_obj
= php_http_client_object_free
;
1168 php_http_client_object_handlers
.clone_obj
= NULL
;
1169 zend_declare_property_null(php_http_client_class_entry
, ZEND_STRL("observers"), ZEND_ACC_PRIVATE
);
1170 zend_declare_property_null(php_http_client_class_entry
, ZEND_STRL("options"), ZEND_ACC_PROTECTED
);
1171 zend_declare_property_null(php_http_client_class_entry
, ZEND_STRL("history"), ZEND_ACC_PROTECTED
);
1172 zend_declare_property_bool(php_http_client_class_entry
, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC
);
1174 zend_hash_init(&php_http_client_drivers
, 2, NULL
, php_http_client_driver_hash_dtor
, 1);
1179 PHP_MSHUTDOWN_FUNCTION(http_client
)
1181 zend_hash_destroy(&php_http_client_drivers
);
1190 * vim600: noet sw=4 ts=4 fdm=marker
1191 * vim<600: noet sw=4 ts=4