+/*
+ * array of name => php_http_client_driver_t*
+ */
+static HashTable php_http_client_drivers;
+
+static void php_http_client_driver_hash_dtor(zval *pData)
+{
+ pefree(Z_PTR_P(pData), 1);
+}
+
+ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver)
+{
+ return zend_hash_add_mem(&php_http_client_drivers, driver->driver_name, (void *) driver, sizeof(php_http_client_driver_t))
+ ? SUCCESS : FAILURE;
+}
+
+php_http_client_driver_t *php_http_client_driver_get(zend_string *name)
+{
+ zval *ztmp;
+ php_http_client_driver_t *tmp;
+
+ if (name && (tmp = zend_hash_find_ptr(&php_http_client_drivers, name))) {
+ return tmp;
+ }
+ if ((ztmp = zend_hash_get_current_data(&php_http_client_drivers))) {
+ return Z_PTR_P(ztmp);
+ }
+ return NULL;
+}
+
+static int apply_driver_list(zval *p, void *arg)
+{
+ php_http_client_driver_t *d = Z_PTR_P(p);
+ zval zname;
+
+ ZVAL_STR_COPY(&zname, d->driver_name);
+
+ zend_hash_next_index_insert(arg, &zname);
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+void php_http_client_driver_list(HashTable *ht)
+{
+ zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht);
+}
+
+void php_http_client_options_set_subr(zval *instance, char *key, size_t len, zval *opts, int overwrite)
+{
+ if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) {
+ zend_class_entry *this_ce = Z_OBJCE_P(instance);
+ zval old_opts_tmp, *old_opts, new_opts, *entry = NULL;
+
+ array_init(&new_opts);
+ old_opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &old_opts_tmp);
+ if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+ array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL(new_opts));
+ }
+
+ if (overwrite) {
+ if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+ Z_ADDREF_P(opts);
+ zend_symtable_str_update(Z_ARRVAL(new_opts), key, len, opts);
+ } else {
+ zend_symtable_str_del(Z_ARRVAL(new_opts), key, len);
+ }
+ } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+ if ((entry = zend_symtable_str_find(Z_ARRVAL(new_opts), key, len))) {
+ array_join(Z_ARRVAL_P(opts), Z_ARRVAL_P(entry), 0, 0);
+ } else {
+ Z_ADDREF_P(opts);
+ zend_symtable_str_update(Z_ARRVAL(new_opts), key, len, opts);
+ }
+ }
+
+ zend_update_property(this_ce, instance, ZEND_STRL("options"), &new_opts);
+ zval_ptr_dtor(&new_opts);
+ }
+}
+
+void php_http_client_options_set(zval *instance, zval *opts)
+{
+ php_http_arrkey_t key;
+ zval new_opts;
+ zend_class_entry *this_ce = Z_OBJCE_P(instance);
+ zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry);
+
+ array_init(&new_opts);
+
+ if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
+ zend_update_property(this_ce, instance, ZEND_STRL("options"), &new_opts);
+ zval_ptr_dtor(&new_opts);
+ } else {
+ zval old_opts_tmp, *old_opts, add_opts, *opt;
+
+ array_init(&add_opts);
+ /* some options need extra attention -- thus cannot use array_merge() directly */
+ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(opts), key.h, key.key, opt)
+ {
+ if (key.key) {
+ if (Z_TYPE_P(opt) == IS_ARRAY && (zend_string_equals_literal(key.key, "ssl") || zend_string_equals_literal(key.key, "cookies"))) {
+ php_http_client_options_set_subr(instance, key.key->val, key.key->len, opt, 0);
+ } else if (is_client && (zend_string_equals_literal(key.key, "recordHistory") || zend_string_equals_literal(key.key, "responseMessageClass"))) {
+ zend_update_property(this_ce, instance, key.key->val, key.key->len, opt);
+ } else if (Z_TYPE_P(opt) == IS_NULL) {
+ old_opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &old_opts_tmp);
+ if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+ zend_symtable_del(Z_ARRVAL_P(old_opts), key.key);
+ }
+ } else {
+ Z_TRY_ADDREF_P(opt);
+ add_assoc_zval_ex(&add_opts, key.key->val, key.key->len, opt);
+ }
+ }
+ }
+ ZEND_HASH_FOREACH_END();
+
+ old_opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &old_opts_tmp);
+ if (Z_TYPE_P(old_opts) == IS_ARRAY) {
+ array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL(new_opts));
+ }
+ array_join(Z_ARRVAL(add_opts), Z_ARRVAL(new_opts), 0, 0);
+ zend_update_property(this_ce, instance, ZEND_STRL("options"), &new_opts);
+ zval_ptr_dtor(&new_opts);
+ zval_ptr_dtor(&add_opts);
+ }
+}
+
+void php_http_client_options_get_subr(zval *instance, char *key, size_t len, zval *return_value)
+{
+ zend_class_entry *this_ce = Z_OBJCE_P(instance);
+ zval *options, opts_tmp, *opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &opts_tmp);
+
+ if ((Z_TYPE_P(opts) == IS_ARRAY) && (options = zend_symtable_str_find(Z_ARRVAL_P(opts), key, len))) {
+ RETVAL_ZVAL_FAST(options);
+ }
+}
+
+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)