+/*
+ * array of name => php_http_client_driver_t*
+ */
+static HashTable php_http_client_drivers;
+
+ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver)
+{
+ return zend_hash_add(&php_http_client_drivers, driver->name_str, driver->name_len + 1, (void *) driver, sizeof(php_http_client_driver_t), NULL);
+}
+
+ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver)
+{
+ php_http_client_driver_t *tmp;
+
+ if ((name_str && SUCCESS == zend_hash_find(&php_http_client_drivers, name_str, name_len + 1, (void *) &tmp))
+ || (SUCCESS == zend_hash_get_current_data(&php_http_client_drivers, (void *) &tmp))) {
+ *driver = *tmp;
+ return SUCCESS;
+ }
+ return FAILURE;
+}
+
+static int apply_driver_list(void *p, void *arg TSRMLS_DC)
+{
+ php_http_client_driver_t *d = p;
+ zval *zname;
+
+ MAKE_STD_ZVAL(zname);
+ ZVAL_STRINGL(zname, d->name_str, d->name_len, 1);
+
+ zend_hash_next_index_insert(arg, &zname, sizeof(zval *), NULL);
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+void php_http_client_driver_list(HashTable *ht TSRMLS_DC)
+{
+ zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht TSRMLS_CC);
+}
+
+void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC)
+{
+ if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) {
+ zend_class_entry *this_ce = Z_OBJCE_P(getThis());
+ zval *old_opts, *new_opts, **entry = NULL;
+
+ MAKE_STD_ZVAL(new_opts);
+ array_init(new_opts);
+ old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+ if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+ array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
+ }
+
+ if (overwrite) {
+ if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+ Z_ADDREF_P(opts);
+ zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
+ } else {
+ zend_symtable_del(Z_ARRVAL_P(new_opts), key, len);
+ }
+ } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+ if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) {
+ array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, 0);
+ } else {
+ Z_ADDREF_P(opts);
+ zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
+ }
+ }
+
+ zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
+ zval_ptr_dtor(&new_opts);
+ }
+}
+
+void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC)
+{
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+ HashPosition pos;
+ zval *new_opts;
+ zend_class_entry *this_ce = Z_OBJCE_P(getThis());
+ zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry TSRMLS_CC);
+
+ MAKE_STD_ZVAL(new_opts);
+ array_init(new_opts);
+
+ if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+ zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
+ zval_ptr_dtor(&new_opts);
+ } else {
+ zval *old_opts, *add_opts, **opt;
+
+ MAKE_STD_ZVAL(add_opts);
+ array_init(add_opts);
+ /* some options need extra attention -- thus cannot use array_merge() directly */
+ FOREACH_KEYVAL(pos, opts, key, opt) {
+ if (key.type == HASH_KEY_IS_STRING) {
+#define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s))
+ if (Z_TYPE_PP(opt) == IS_ARRAY && (KEYMATCH(key, "ssl") || KEYMATCH(key, "cookies"))) {
+ php_http_client_options_set_subr(getThis(), key.str, key.len, *opt, 0 TSRMLS_CC);
+ } else if (is_client && (KEYMATCH(key, "recordHistory") || KEYMATCH(key, "responseMessageClass"))) {
+ zend_update_property(this_ce, getThis(), key.str, key.len-1, *opt TSRMLS_CC);
+ } else if (Z_TYPE_PP(opt) == IS_NULL) {
+ old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+ if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+ zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len);
+ }
+ } else {
+ Z_ADDREF_P(*opt);
+ add_assoc_zval_ex(add_opts, key.str, key.len, *opt);
+ }
+ }
+ }
+
+ old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+ if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+ array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
+ }
+ array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0);
+ zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
+ zval_ptr_dtor(&new_opts);
+ zval_ptr_dtor(&add_opts);
+ }
+}
+
+void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC)
+{
+ zend_class_entry *this_ce = Z_OBJCE_P(getThis());
+ zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
+
+ if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) {
+ RETVAL_ZVAL(*options, 1, 0);
+ }
+}
+
+static void queue_dtor(void *enqueued)
+{
+ php_http_client_enqueue_t *e = enqueued;
+
+ if (e->dtor) {
+ e->dtor(e);
+ }
+}
+
+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 TSRMLS_DC)