Merge branch 'v2.6.x'
authorMichael Wallner <mike@php.net>
Mon, 13 Jun 2016 12:54:41 +0000 (14:54 +0200)
committerMichael Wallner <mike@php.net>
Mon, 13 Jun 2016 12:54:41 +0000 (14:54 +0200)
22 files changed:
src/php_http.c
src/php_http_api.h
src/php_http_client.c
src/php_http_client_curl.c
src/php_http_client_curl.h
src/php_http_client_curl_event.c [new file with mode: 0644]
src/php_http_client_curl_event.h [new file with mode: 0644]
src/php_http_client_curl_user.c [new file with mode: 0644]
src/php_http_client_curl_user.h [new file with mode: 0644]
src/php_http_message_body.c
src/php_http_negotiate.c
src/php_http_params.c
src/php_http_querystring.c
tests/bug61444.phpt
tests/client018.phpt
tests/client022.phpt
tests/client028.phpt [new file with mode: 0644]
tests/header005.phpt
tests/helper/pipeline.inc
tests/negotiate001.phpt
tests/params017.phpt
tests/url002.phpt

index f9bb4d09c886f4e049e852d9b983e38194821eee..3b3338f260ad7fabcd92c9d1ad2f37ffe94e3a3f 100644 (file)
@@ -143,6 +143,7 @@ PHP_MINIT_FUNCTION(http)
 #if PHP_HTTP_HAVE_CURL
        || SUCCESS != PHP_MINIT_CALL(http_curl)
        || SUCCESS != PHP_MINIT_CALL(http_client_curl)
+       || SUCCESS != PHP_MINIT_CALL(http_client_curl_user)
 #endif
        || SUCCESS != PHP_MINIT_CALL(http_url)
        || SUCCESS != PHP_MINIT_CALL(http_env)
index e8c175fa6784df0e8a0d80ed2b1dd727fa3d9493..b26469458b06ca77aed704dbe4c4966dddcec644 100644 (file)
 #include "php_http_client_request.h"
 #include "php_http_client_response.h"
 #include "php_http_client_curl.h"
+#include "php_http_client_curl_user.h"
+#include "php_http_client_curl_event.h"
 #include "php_http_url.h"
 #include "php_http_version.h"
 
index 4b465c0dc2bb0db8f8474fe4184040706747414f..5d2aafe156f1c692c18618a3e2b6d8ea705b75e9 100644 (file)
@@ -363,19 +363,31 @@ static HashTable *php_http_client_object_get_gc(zval *object, zval **table, int
        php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, object);
        zend_llist_element *el = NULL;
        HashTable *props = Z_OBJPROP_P(object);
-       uint32_t count = zend_hash_num_elements(props) + zend_llist_count(&obj->client->responses) + zend_llist_count(&obj->client->requests);
+       uint32_t count = zend_hash_num_elements(props) + zend_llist_count(&obj->client->responses) + zend_llist_count(&obj->client->requests) + 1;
        zval *val;
 
        *n = 0;
        *table = obj->gc = erealloc(obj->gc, sizeof(zval) * count);
 
+#if PHP_HTTP_HAVE_CURL
+       if (obj->client->ops == php_http_client_curl_get_ops()) {
+               php_http_client_curl_t *curl = obj->client->ctx;
+
+               if (curl->ev_ops == php_http_client_curl_user_ops_get()) {
+                       php_http_client_curl_user_context_t *ctx = curl->ev_ctx;
+
+                       ZVAL_COPY_VALUE(&obj->gc[(*n)++], &ctx->user);
+               }
+       }
+#endif
+
        for (el = obj->client->responses.head; el; el = el->next) {
                php_http_message_object_t *response_obj = *(php_http_message_object_t **) el->data;
                ZVAL_OBJ(&obj->gc[(*n)++], &response_obj->zo);
        }
 
        for (el = obj->client->requests.head; el; el = el->next) {
-               php_http_client_enqueue_t *q = *(php_http_client_enqueue_t **) el->data;
+               php_http_client_enqueue_t *q = (php_http_client_enqueue_t *) el->data;
                php_http_message_object_t *request_obj = q->opaque; /* FIXME */
                ZVAL_OBJ(&obj->gc[(*n)++], &request_obj->zo);
        }
index 423e12e3c6214c3e5f034e8b5e4c5e375f7dbc4a..be1783252a5a98a6fe843c0af8095f8d1cf52f61 100644 (file)
 
 #include "php_http_api.h"
 #include "php_http_client.h"
+#include "php_http_client_curl_event.h"
+#include "php_http_client_curl_user.h"
 
 #if PHP_HTTP_HAVE_CURL
 
-#if PHP_HTTP_HAVE_EVENT
-#      if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000)
-#              include <event.h>
-#              define event_base_new event_init
-#              define event_assign(e, b, s, a, cb, d) do {\
-                       event_set(e, s, a, cb, d); \
-                       event_base_set(b, e); \
-               } while(0)
-#      else
-#              if PHP_HTTP_HAVE_EVENT2
-#                      include <event2/event.h>
-#                      include <event2/event_struct.h>
-#              else
-#                      error "libevent presence is unknown"
-#              endif
-#      endif
-#      ifndef DBG_EVENTS
-#              define DBG_EVENTS 0
-#      endif
-#endif
-
 #ifdef PHP_HTTP_HAVE_OPENSSL
 #      include <openssl/ssl.h>
 #endif
 #      include <gnutls.h>
 #endif
 
-typedef struct php_http_client_curl_handle {
-       CURLM *multi;
-       CURLSH *share;
-} php_http_client_curl_handle_t;
-
-typedef struct php_http_client_curl {
-       php_http_client_curl_handle_t *handle;
-
-       int unfinished;  /* int because of curl_multi_perform() */
-
-#if PHP_HTTP_HAVE_EVENT
-       struct event_base *evbase;
-       struct event *timeout;
-       unsigned useevents:1;
-#endif
-} php_http_client_curl_t;
-
 typedef struct php_http_client_curl_handler {
        CURL *handle;
        php_resource_factory_t *rf;
@@ -677,7 +641,7 @@ static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_ha
        return response;
 }
 
-static void php_http_curlm_responsehandler(php_http_client_t *context)
+void php_http_client_curl_responsehandler(php_http_client_t *context)
 {
        int err_count = 0, remaining = 0;
        php_http_curle_storage_t *st, *err = NULL;
@@ -729,158 +693,26 @@ static void php_http_curlm_responsehandler(php_http_client_t *context)
        }
 }
 
-#if PHP_HTTP_HAVE_EVENT
-
-typedef struct php_http_curlm_event {
-       struct event evnt;
-       php_http_client_t *context;
-} php_http_curlm_event_t;
-
-static inline int etoca(short action) {
-       switch (action & (EV_READ|EV_WRITE)) {
-               case EV_READ:
-                       return CURL_CSELECT_IN;
-                       break;
-               case EV_WRITE:
-                       return CURL_CSELECT_OUT;
-                       break;
-               case EV_READ|EV_WRITE:
-                       return CURL_CSELECT_IN|CURL_CSELECT_OUT;
-                       break;
-               default:
-                       return 0;
-       }
-}
-
-static void php_http_curlm_timeout_callback(int socket, short action, void *event_data)
-{
-       php_http_client_t *context = event_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "T");
-#endif
-       if (curl->useevents) {
-               CURLMcode rc;
-
-               /* ignore and use -1,0 on timeout */
-               (void) socket;
-               (void) action;
-
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
-
-               if (CURLM_OK != rc) {
-                       php_error_docref(NULL, E_WARNING, "%s",  curl_multi_strerror(rc));
-               }
-
-               php_http_curlm_responsehandler(context);
-       }
-}
-
-static void php_http_curlm_event_callback(int socket, short action, void *event_data)
+void php_http_client_curl_loop(php_http_client_t *client, curl_socket_t s, int curl_action)
 {
-       php_http_client_t *context = event_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "E");
-#endif
-       if (curl->useevents) {
-               CURLMcode rc = CURLM_OK;
-
-               while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, socket, etoca(action), &curl->unfinished)));
-
-               if (CURLM_OK != rc) {
-                       php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
-               }
-
-               php_http_curlm_responsehandler(context);
-
-               /* remove timeout if there are no transfers left */
-               if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       event_del(curl->timeout);
-               }
-       }
-}
-
-static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
-{
-       php_http_client_t *context = socket_data;
-       php_http_client_curl_t *curl = context->ctx;
+       CURLMcode rc;
+       php_http_client_curl_t *curl = client->ctx;
 
 #if DBG_EVENTS
-       fprintf(stderr, "S");
+       fprintf(stderr, "H");
 #endif
-       if (curl->useevents) {
-               int events = EV_PERSIST;
-               php_http_curlm_event_t *ev = assign_data;
-
-               if (!ev) {
-                       ev = ecalloc(1, sizeof(php_http_curlm_event_t));
-                       ev->context = context;
-                       curl_multi_assign(curl->handle->multi, sock, ev);
-               } else {
-                       event_del(&ev->evnt);
-               }
 
-               switch (action) {
-                       case CURL_POLL_IN:
-                               events |= EV_READ;
-                               break;
-                       case CURL_POLL_OUT:
-                               events |= EV_WRITE;
-                               break;
-                       case CURL_POLL_INOUT:
-                               events |= EV_READ|EV_WRITE;
-                               break;
-
-                       case CURL_POLL_REMOVE:
-                               efree(ev);
-                               /* no break */
-                       case CURL_POLL_NONE:
-                               return 0;
-
-                       default:
-                               php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
-                               return -1;
-               }
+       do {
+               rc = curl_multi_socket_action(curl->handle->multi, s, curl_action, &curl->unfinished);
+       } while (CURLM_CALL_MULTI_PERFORM == rc);
 
-               event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
-               event_add(&ev->evnt, NULL);
+       if (CURLM_OK != rc) {
+               php_error_docref(NULL, E_WARNING, "%s",  curl_multi_strerror(rc));
        }
 
-       return 0;
+       php_http_client_curl_responsehandler(client);
 }
 
-static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
-{
-       php_http_client_t *context = timer_data;
-       php_http_client_curl_t *curl = context->ctx;
-
-#if DBG_EVENTS
-       fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
-#endif
-       if (curl->useevents) {
-
-               if (timeout_ms < 0) {
-                       php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context);
-               } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       struct timeval timeout;
-
-                       if (!event_initialized(curl->timeout)) {
-                               event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
-                       }
-
-                       timeout.tv_sec = timeout_ms / 1000;
-                       timeout.tv_usec = (timeout_ms % 1000) * 1000;
-
-                       event_add(curl->timeout, &timeout);
-               }
-       }
-}
-
-#endif /* HAVE_EVENT */
-
 /* curl options */
 
 static php_http_options_t php_http_curle_options, php_http_curlm_options;
@@ -1746,27 +1578,24 @@ static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_
 }
 #endif
 
-#if PHP_HTTP_HAVE_EVENT
-static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
+static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, php_http_client_curl_ops_t *ev_ops, zval *init_data)
 {
        php_http_client_curl_t *curl = h->ctx;
+       void *ev_ctx;
 
-       if ((curl->useevents = enable)) {
-               if (!curl->evbase) {
-                       curl->evbase = event_base_new();
-               }
-               if (!curl->timeout) {
-                       curl->timeout = ecalloc(1, sizeof(struct event));
+       if (ev_ops) {
+               if (!(ev_ctx = ev_ops->init(h, init_data))) {
+                       return FAILURE;
                }
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, h);
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback);
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, h);
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback);
+               curl->ev_ctx = ev_ctx;
+               curl->ev_ops = ev_ops;
        } else {
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
-               curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
+               if (curl->ev_ops) {
+                       if (curl->ev_ctx) {
+                               curl->ev_ops->dtor(&curl->ev_ctx);
+                       }
+                       curl->ev_ops = NULL;
+               }
        }
 
        return SUCCESS;
@@ -1775,10 +1604,18 @@ static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h
 static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata)
 {
        php_http_client_t *client = userdata;
+       php_http_client_curl_ops_t *ev_ops = NULL;
 
-       return php_http_curlm_use_eventloop(client, value && Z_TYPE_P(value) == IS_TRUE);
-}
+       if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_client_curl_user_get_class_entry())) {
+               ev_ops = php_http_client_curl_user_ops_get();
+#if PHP_HTTP_HAVE_EVENT
+       } else if (value && zend_is_true(value)) {
+               ev_ops = php_http_client_curl_event_ops_get();
 #endif
+       }
+
+       return php_http_curlm_use_eventloop(client, ev_ops, value);
+}
 
 static ZEND_RESULT_CODE php_http_curlm_option_set_share_cookies(php_http_option_t *opt, zval *value, void *userdata)
 {
@@ -1866,11 +1703,9 @@ static void php_http_curlm_options_init(php_http_options_t *registry)
        }
 #endif
        /* events */
-#if PHP_HTTP_HAVE_EVENT
-       if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, _IS_BOOL))) {
+       if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, 0))) {
                opt->setter = php_http_curlm_option_set_use_eventloop;
        }
-#endif
        /* share */
        if ((opt = php_http_option_register(registry, ZEND_STRL("share_cookies"), 0, _IS_BOOL))) {
                opt->setter = php_http_curlm_option_set_share_cookies;
@@ -2219,19 +2054,9 @@ static void php_http_client_curl_dtor(php_http_client_t *h)
 {
        php_http_client_curl_t *curl = h->ctx;
 
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->timeout) {
-               if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       event_del(curl->timeout);
-               }
-               efree(curl->timeout);
-               curl->timeout = NULL;
-       }
-       if (curl->evbase) {
-               event_base_free(curl->evbase);
-               curl->evbase = NULL;
+       if (curl->ev_ops) {
+               curl->ev_ops->dtor(&curl->ev_ctx);
        }
-#endif
        curl->unfinished = 0;
 
        php_resource_factory_handle_dtor(h->rf, curl->handle);
@@ -2366,17 +2191,6 @@ static void php_http_client_curl_reset(php_http_client_t *h)
        }
 }
 
-static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
-{
-       if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) {
-               timeout->tv_sec = max_tout / 1000;
-               timeout->tv_usec = (max_tout % 1000) * 1000;
-       } else {
-               timeout->tv_sec = 0;
-               timeout->tv_usec = 1000;
-       }
-}
-
 #ifdef PHP_WIN32
 #      define SELECT_ERROR SOCKET_ERROR
 #else
@@ -2390,22 +2204,9 @@ static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct t
        struct timeval timeout;
        php_http_client_curl_t *curl = h->ctx;
 
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->useevents) {
-               if (!event_initialized(curl->timeout)) {
-                       event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h);
-               } else if (custom_timeout && timerisset(custom_timeout)) {
-                       event_add(curl->timeout, custom_timeout);
-               } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
-                       php_http_client_curl_get_timeout(curl, 1000, &timeout);
-                       event_add(curl->timeout, &timeout);
-               }
-
-               event_base_loop(curl->evbase, EVLOOP_ONCE);
-
-               return SUCCESS;
+       if (curl->ev_ops) {
+               return curl->ev_ops->wait(curl->ev_ctx, custom_timeout);
        }
-#endif
 
        FD_ZERO(&R);
        FD_ZERO(&W);
@@ -2432,14 +2233,13 @@ static int php_http_client_curl_once(php_http_client_t *h)
 {
        php_http_client_curl_t *curl = h->ctx;
 
-#if PHP_HTTP_HAVE_EVENT
-       if (curl->useevents) {
-               event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
-       } else
-#endif
-       while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished));
+       if (curl->ev_ops) {
+               curl->ev_ops->once(curl->ev_ctx);
+       } else {
+               while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished));
+       }
 
-       php_http_curlm_responsehandler(h);
+       php_http_client_curl_responsehandler(h);
 
        return curl->unfinished;
 
@@ -2447,36 +2247,21 @@ static int php_http_client_curl_once(php_http_client_t *h)
 
 static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h)
 {
-#if PHP_HTTP_HAVE_EVENT
        php_http_client_curl_t *curl = h->ctx;
 
-       if (curl->useevents) {
-               php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h);
-               do {
-                       int ev_rc = event_base_dispatch(curl->evbase);
-
-#if DBG_EVENTS
-                       fprintf(stderr, "%c", "X.0"[ev_rc+1]);
-#endif
+       if (curl->ev_ops) {
+               return curl->ev_ops->exec(curl->ev_ctx);
+       }
 
-                       if (ev_rc < 0) {
-                               php_error_docref(NULL, E_ERROR, "Error in event_base_dispatch()");
-                               return FAILURE;
-                       }
-               } while (curl->unfinished && !EG(exception));
-       } else
-#endif
-       {
-               while (php_http_client_curl_once(h) && !EG(exception)) {
-                       if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
+       while (php_http_client_curl_once(h) && !EG(exception)) {
+               if (SUCCESS != php_http_client_curl_wait(h, NULL)) {
 #ifdef PHP_WIN32
-                               /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
-                               php_error_docref(NULL, E_WARNING, "WinSock error: %d", WSAGetLastError());
+                       /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
+                       php_error_docref(NULL, E_WARNING, "WinSock error: %d", WSAGetLastError());
 #else
-                               php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
+                       php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
 #endif
-                               return FAILURE;
-                       }
+                       return FAILURE;
                }
        }
 
@@ -2500,7 +2285,9 @@ static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_ht
 
                case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
 #if PHP_HTTP_HAVE_EVENT
-                       return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
+                       return php_http_curlm_use_eventloop(h, (*(zend_bool *) arg)
+                                       ? php_http_client_curl_event_ops_get()
+                                       : NULL, NULL);
                        break;
 #endif
 
@@ -2598,6 +2385,7 @@ php_http_client_ops_t *php_http_client_curl_get_ops(void)
        return &php_http_client_curl_ops;
 }
 
+
 PHP_MINIT_FUNCTION(http_client_curl)
 {
        curl_version_info_data *info;
index 91286477d76514b4511ecd37d1fb9844a3aadf4a..abd8f993085b28f18920659e1e71e4e899da4415 100644 (file)
@@ -19,8 +19,46 @@ struct php_http_client_curl_globals {
        php_http_client_driver_t driver;
 };
 
+typedef struct php_http_client_curl_handle {
+       CURLM *multi;
+       CURLSH *share;
+} php_http_client_curl_handle_t;
+
+typedef struct php_http_client_curl_ops {
+       void *(*init)();
+       void (*dtor)(void **ctx_ptr);
+       ZEND_RESULT_CODE (*once)(void *ctx);
+       ZEND_RESULT_CODE (*wait)(void *ctx, struct timeval *custom_timeout);
+       ZEND_RESULT_CODE (*exec)(void *ctx);
+} php_http_client_curl_ops_t;
+
+typedef struct php_http_client_curl {
+       php_http_client_curl_handle_t *handle;
+
+       int unfinished;  /* int because of curl_multi_perform() */
+
+       void *ev_ctx;
+       php_http_client_curl_ops_t *ev_ops;
+} php_http_client_curl_t;
+
+static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout)
+{
+       if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) {
+               timeout->tv_sec = max_tout / 1000;
+               timeout->tv_usec = (max_tout % 1000) * 1000;
+       } else {
+               timeout->tv_sec = 0;
+               timeout->tv_usec = 1000;
+       }
+}
+
+PHP_HTTP_API void php_http_client_curl_responsehandler(php_http_client_t *client);
+PHP_HTTP_API void php_http_client_curl_loop(php_http_client_t *client, curl_socket_t s, int curl_action);
+PHP_HTTP_API php_http_client_ops_t *php_http_client_curl_get_ops(void);
+
 PHP_MINIT_FUNCTION(http_client_curl);
 PHP_MSHUTDOWN_FUNCTION(http_client_curl);
+
 #endif /* PHP_HTTP_HAVE_CURL */
 
 #endif /* PHP_HTTP_CLIENT_CURL_H */
diff --git a/src/php_http_client_curl_event.c b/src/php_http_client_curl_event.c
new file mode 100644 (file)
index 0000000..e1ba505
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#include "php_http_api.h"
+
+#if PHP_HTTP_HAVE_CURL
+#if PHP_HTTP_HAVE_EVENT
+#      if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000)
+#              include <event.h>
+#              define event_base_new event_init
+#              define event_assign(e, b, s, a, cb, d) do {\
+                       event_set(e, s, a, cb, d); \
+                       event_base_set(b, e); \
+               } while(0)
+#      else
+#              if PHP_HTTP_HAVE_EVENT2
+#                      include <event2/event.h>
+#                      include <event2/event_struct.h>
+#              else
+#                      error "libevent presence is unknown"
+#              endif
+#      endif
+#      ifndef DBG_EVENTS
+#              define DBG_EVENTS 0
+#      endif
+
+typedef struct php_http_client_curl_event_context {
+       php_http_client_t *client;
+       struct event_base *evbase;
+       struct event *timeout;
+} php_http_client_curl_event_context_t;
+
+typedef struct php_http_client_curl_event_ev {
+       struct event evnt;
+       php_http_client_curl_event_context_t *context;
+} php_http_client_curl_event_ev_t;
+
+static inline int etoca(short action) {
+       switch (action & (EV_READ|EV_WRITE)) {
+               case EV_READ:
+                       return CURL_CSELECT_IN;
+                       break;
+               case EV_WRITE:
+                       return CURL_CSELECT_OUT;
+                       break;
+               case EV_READ|EV_WRITE:
+                       return CURL_CSELECT_IN|CURL_CSELECT_OUT;
+                       break;
+               default:
+                       return 0;
+       }
+}
+
+static void php_http_client_curl_event_handler(void *context, curl_socket_t s, int curl_action)
+{
+       CURLMcode rc;
+       php_http_client_curl_event_context_t *ctx = context;
+       php_http_client_curl_t *curl = ctx->client->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "H");
+#endif
+
+       do {
+               rc = curl_multi_socket_action(curl->handle->multi, s, curl_action, &curl->unfinished);
+       } while (CURLM_CALL_MULTI_PERFORM == rc);
+
+       if (CURLM_OK != rc) {
+               php_error_docref(NULL, E_WARNING, "%s",  curl_multi_strerror(rc));
+       }
+
+       php_http_client_curl_responsehandler(ctx->client);
+}
+
+static void php_http_client_curl_event_timeout_callback(int socket, short action, void *event_data)
+{
+#if DBG_EVENTS
+       fprintf(stderr, "T");
+#endif
+
+       /* ignore and use -1,0 on timeout */
+       (void) socket;
+       (void) action;
+
+       php_http_client_curl_event_handler(event_data, CURL_SOCKET_TIMEOUT, 0);
+}
+
+static void php_http_client_curl_event_timer(CURLM *multi, long timeout_ms, void *timer_data)
+{
+       php_http_client_curl_event_context_t *context = timer_data;
+
+#if DBG_EVENTS
+       fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
+#endif
+
+       if (timeout_ms < 0) {
+               php_http_client_curl_event_handler(context, CURL_SOCKET_TIMEOUT, 0);
+       } else if (timeout_ms > 0 || !event_initialized(context->timeout) || !event_pending(context->timeout, EV_TIMEOUT, NULL)) {
+               struct timeval timeout;
+
+               if (!event_initialized(context->timeout)) {
+                       event_assign(context->timeout, context->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, context);
+               }
+
+               timeout.tv_sec = timeout_ms / 1000;
+               timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+               event_add(context->timeout, &timeout);
+       }
+}
+
+static void php_http_client_curl_event_callback(int socket, short action, void *event_data)
+{
+       php_http_client_curl_event_context_t *ctx = event_data;
+       php_http_client_curl_t *curl = ctx->client->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "E");
+#endif
+
+       php_http_client_curl_event_handler(event_data, socket, etoca(action));
+
+       /* remove timeout if there are no transfers left */
+       if (!curl->unfinished && event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
+               event_del(ctx->timeout);
+       }
+}
+
+static int php_http_client_curl_event_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
+{
+       php_http_client_curl_event_context_t *ctx = socket_data;
+       php_http_client_curl_t *curl = ctx->client->ctx;
+       int events = EV_PERSIST;
+       php_http_client_curl_event_ev_t *ev = assign_data;
+
+#if DBG_EVENTS
+       fprintf(stderr, "S");
+#endif
+
+       if (!ev) {
+               ev = ecalloc(1, sizeof(*ev));
+               ev->context = ctx;
+               curl_multi_assign(curl->handle->multi, sock, ev);
+       } else {
+               event_del(&ev->evnt);
+       }
+
+       switch (action) {
+               case CURL_POLL_IN:
+                       events |= EV_READ;
+                       break;
+               case CURL_POLL_OUT:
+                       events |= EV_WRITE;
+                       break;
+               case CURL_POLL_INOUT:
+                       events |= EV_READ|EV_WRITE;
+                       break;
+
+               case CURL_POLL_REMOVE:
+                       efree(ev);
+                       /* no break */
+               case CURL_POLL_NONE:
+                       return 0;
+
+               default:
+                       php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
+                       return -1;
+       }
+
+       event_assign(&ev->evnt, ctx->evbase, sock, events, php_http_client_curl_event_callback, ctx);
+       event_add(&ev->evnt, NULL);
+
+       return 0;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_event_once(void *context)
+{
+       php_http_client_curl_event_context_t *ctx = context;
+
+#if DBG_EVENTS
+       fprintf(stderr, "O");
+#endif
+
+       if (0 > event_base_loop(ctx->evbase, EVLOOP_NONBLOCK)) {
+               return FAILURE;
+       }
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_event_wait(void *context, struct timeval *custom_timeout)
+{
+       php_http_client_curl_event_context_t *ctx = context;
+       struct timeval timeout;
+
+#if DBG_EVENTS
+       fprintf(stderr, "W");
+#endif
+
+       if (!event_initialized(ctx->timeout)) {
+               if (0 > event_assign(ctx->timeout, ctx->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, ctx)) {
+                       return FAILURE;
+               }
+       } else if (custom_timeout && timerisset(custom_timeout)) {
+               if (0 > event_add(ctx->timeout, custom_timeout)) {
+                       return FAILURE;
+               }
+       } else if (!event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
+               php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout);
+               if (0 > event_add(ctx->timeout, &timeout)) {
+                       return FAILURE;
+               }
+       }
+
+       if (0 > event_base_loop(ctx->evbase, EVLOOP_ONCE)) {
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_event_exec(void *context)
+{
+       php_http_client_curl_event_context_t *ctx = context;
+       php_http_client_curl_t *curl = ctx->client->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "E");
+#endif
+
+       /* kickstart */
+       php_http_client_curl_event_handler(ctx, CURL_SOCKET_TIMEOUT, 0);
+
+       do {
+               if (0 > event_base_dispatch(ctx->evbase)) {
+                       return FAILURE;
+               }
+       } while (curl->unfinished && !EG(exception));
+
+       return SUCCESS;
+}
+
+static void *php_http_client_curl_event_init(php_http_client_t *client)
+{
+       php_http_client_curl_t *curl = client->ctx;
+       php_http_client_curl_event_context_t *ctx;
+       struct event_base *evb = event_base_new();
+
+#if DBG_EVENTS
+       fprintf(stderr, "I");
+#endif
+
+       if (!evb) {
+               return NULL;
+       }
+
+       ctx = ecalloc(1, sizeof(*ctx));
+       ctx->client = client;
+       ctx->evbase = evb;
+       ctx->timeout = ecalloc(1, sizeof(struct event));
+
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_event_socket);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_event_timer);
+
+       return ctx;
+}
+
+static void php_http_client_curl_event_dtor(void **context)
+{
+       php_http_client_curl_event_context_t *ctx = *context;
+       php_http_client_curl_t *curl;
+
+#if DBG_EVENTS
+       fprintf(stderr, "D");
+#endif
+
+       curl = ctx->client->ctx;
+
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
+
+       if (event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
+               event_del(ctx->timeout);
+       }
+       efree(ctx->timeout);
+       event_base_free(ctx->evbase);
+
+       efree(ctx);
+       *context = NULL;
+}
+
+static php_http_client_curl_ops_t php_http_client_curl_event_ops = {
+       &php_http_client_curl_event_init,
+       &php_http_client_curl_event_dtor,
+       &php_http_client_curl_event_once,
+       &php_http_client_curl_event_wait,
+       &php_http_client_curl_event_exec,
+};
+
+php_http_client_curl_ops_t *php_http_client_curl_event_ops_get()
+{
+       return &php_http_client_curl_event_ops;
+}
+
+#endif /* PHP_HTTP_HAVE_EVENT */
+#endif /* PHP_HTTP_HAVE_CURL */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/src/php_http_client_curl_event.h b/src/php_http_client_curl_event.h
new file mode 100644 (file)
index 0000000..4739f62
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_CLIENT_CURL_EVENT_H
+#define PHP_HTTP_CLIENT_CURL_EVENT_H
+
+#if PHP_HTTP_HAVE_CURL
+#if PHP_HTTP_HAVE_EVENT
+
+php_http_client_curl_ops_t *php_http_client_curl_event_ops_get();
+
+#endif
+#endif
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/src/php_http_client_curl_user.c b/src/php_http_client_curl_user.c
new file mode 100644 (file)
index 0000000..a30d666
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#include "php_http_api.h"
+
+#include "php_network.h"
+#include "zend_closures.h"
+
+#if PHP_HTTP_HAVE_CURL
+
+typedef struct php_http_client_curl_user_ev {
+       php_stream *socket;
+       php_http_client_curl_user_context_t *context;
+} php_http_client_curl_user_ev_t;
+
+static void php_http_client_curl_user_handler(INTERNAL_FUNCTION_PARAMETERS)
+{
+       zval *zstream = NULL, *zclient = NULL;
+       php_stream *stream = NULL;
+       long action = 0;
+       php_socket_t fd = CURL_SOCKET_TIMEOUT;
+       php_http_client_object_t *client = NULL;
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "O|rl", &zclient, php_http_client_get_class_entry(), &zstream, &action)) {
+               return;
+       }
+
+       client = PHP_HTTP_OBJ(NULL, zclient);
+       if (zstream) {
+               php_stream_from_zval(stream, zstream);
+
+               if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void *) &fd, 1)) {
+                       return;
+               }
+       }
+       php_http_client_curl_loop(client->client, fd, action);
+}
+
+static void php_http_client_curl_user_timer(CURLM *multi, long timeout_ms, void *timer_data)
+{
+       php_http_client_curl_user_context_t *context = timer_data;
+
+#if DBG_EVENTS
+       fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
+#endif
+
+       if (timeout_ms <= 0) {
+               php_http_client_curl_loop(context->client, CURL_SOCKET_TIMEOUT, 0);
+       } else if (timeout_ms > 0) {
+               zval args[1], *ztimeout = &args[0];
+
+               ZVAL_LONG(ztimeout, timeout_ms);
+               php_http_object_method_call(&context->timer, &context->user, NULL, 1, args);
+               zval_ptr_dtor(ztimeout);
+       }
+}
+
+static int php_http_client_curl_user_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
+{
+       php_http_client_curl_user_context_t *ctx = socket_data;
+       php_http_client_curl_t *curl = ctx->client->ctx;
+       php_http_client_curl_user_ev_t *ev = assign_data;
+       zval args[2], *zaction = &args[1], *zsocket = &args[0];
+
+#if DBG_EVENTS
+       fprintf(stderr, "S");
+#endif
+
+       if (!ev) {
+               ev = ecalloc(1, sizeof(*ev));
+               ev->context = ctx;
+               ev->socket = php_stream_sock_open_from_socket(sock, NULL);
+
+               curl_multi_assign(curl->handle->multi, sock, ev);
+       }
+
+       switch (action) {
+               case CURL_POLL_IN:
+               case CURL_POLL_OUT:
+               case CURL_POLL_INOUT:
+               case CURL_POLL_REMOVE:
+               case CURL_POLL_NONE:
+                       php_stream_to_zval(ev->socket, zsocket);
+                       Z_TRY_ADDREF_P(zsocket);
+                       ZVAL_LONG(zaction, action);
+                       php_http_object_method_call(&ctx->socket, &ctx->user, NULL, 2, args);
+                       zval_ptr_dtor(zsocket);
+                       zval_ptr_dtor(zaction);
+                       break;
+
+               default:
+                       php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
+                       return -1;
+       }
+
+       if (action == CURL_POLL_REMOVE && ev) {
+               php_stream_close(ev->socket);
+               efree(ev);
+               curl_multi_assign(curl->handle->multi, sock, NULL);
+       }
+       return 0;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_user_once(void *context)
+{
+       php_http_client_curl_user_context_t *ctx = context;
+
+#if DBG_EVENTS
+       fprintf(stderr, "O");
+#endif
+
+       return php_http_object_method_call(&ctx->once, &ctx->user, NULL, 0, NULL);
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_user_wait(void *context, struct timeval *custom_timeout)
+{
+       php_http_client_curl_user_context_t *ctx = context;
+       struct timeval timeout;
+       zval args[1], *ztimeout = &args[0];
+       ZEND_RESULT_CODE rv;
+
+#if DBG_EVENTS
+       fprintf(stderr, "W");
+#endif
+
+       if (!custom_timeout || !timerisset(custom_timeout)) {
+               php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout);
+               custom_timeout = &timeout;
+       }
+
+       ZVAL_LONG(ztimeout, custom_timeout->tv_sec * 1000 + custom_timeout->tv_usec / 1000);
+       rv = php_http_object_method_call(&ctx->wait, &ctx->user, NULL, 1, args);
+       zval_ptr_dtor(ztimeout);
+
+       return rv;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_user_exec(void *context)
+{
+       php_http_client_curl_user_context_t *ctx = context;
+       php_http_client_curl_t *curl = ctx->client->ctx;
+
+#if DBG_EVENTS
+       fprintf(stderr, "E");
+#endif
+
+       /* kickstart */
+       php_http_client_curl_loop(ctx->client, CURL_SOCKET_TIMEOUT, 0);
+
+       do {
+               if (SUCCESS != php_http_object_method_call(&ctx->send, &ctx->user, NULL, 0, NULL)) {
+                       return FAILURE;
+               }
+       } while (curl->unfinished && !EG(exception));
+
+       return SUCCESS;
+}
+
+static void *php_http_client_curl_user_init(php_http_client_t *client, void *user_data)
+{
+       php_http_client_curl_t *curl = client->ctx;
+       php_http_client_curl_user_context_t *ctx;
+       php_http_object_method_t init;
+       zval args[1], *zclosure = &args[0];
+
+#if DBG_EVENTS
+       fprintf(stderr, "I");
+#endif
+
+       ctx = ecalloc(1, sizeof(*ctx));
+       ctx->client = client;
+       ZVAL_COPY(&ctx->user, user_data);
+
+       memset(&ctx->closure, 0, sizeof(ctx->closure));
+       ctx->closure.common.type = ZEND_INTERNAL_FUNCTION;
+       ctx->closure.common.function_name = zend_string_init(ZEND_STRL("php_http_client_curl_user_handler"), 0);
+       ctx->closure.internal_function.handler = php_http_client_curl_user_handler;
+
+       zend_create_closure(zclosure, &ctx->closure, NULL, NULL, NULL);
+
+       php_http_object_method_init(&init, &ctx->user, ZEND_STRL("init"));
+       php_http_object_method_call(&init, &ctx->user, NULL, 1, args);
+       php_http_object_method_dtor(&init);
+       zval_ptr_dtor(zclosure);
+
+       php_http_object_method_init(&ctx->timer, &ctx->user, ZEND_STRL("timer"));
+       php_http_object_method_init(&ctx->socket, &ctx->user, ZEND_STRL("socket"));
+       php_http_object_method_init(&ctx->once, &ctx->user, ZEND_STRL("once"));
+       php_http_object_method_init(&ctx->wait, &ctx->user, ZEND_STRL("wait"));
+       php_http_object_method_init(&ctx->send, &ctx->user, ZEND_STRL("send"));
+
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_user_socket);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_user_timer);
+
+       return ctx;
+}
+
+static void php_http_client_curl_user_dtor(void **context)
+{
+       php_http_client_curl_user_context_t *ctx = *context;
+       php_http_client_curl_t *curl;
+
+#if DBG_EVENTS
+       fprintf(stderr, "D");
+#endif
+
+       curl = ctx->client->ctx;
+
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
+       curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
+
+       php_http_object_method_dtor(&ctx->timer);
+       php_http_object_method_dtor(&ctx->socket);
+       php_http_object_method_dtor(&ctx->once);
+       php_http_object_method_dtor(&ctx->wait);
+       php_http_object_method_dtor(&ctx->send);
+
+       zend_string_release(ctx->closure.common.function_name);
+       zval_ptr_dtor(&ctx->user);
+
+       efree(ctx);
+       *context = NULL;
+}
+
+static php_http_client_curl_ops_t php_http_client_curl_user_ops = {
+       &php_http_client_curl_user_init,
+       &php_http_client_curl_user_dtor,
+       &php_http_client_curl_user_once,
+       &php_http_client_curl_user_wait,
+       &php_http_client_curl_user_exec,
+};
+
+php_http_client_curl_ops_t *php_http_client_curl_user_ops_get()
+{
+       return &php_http_client_curl_user_ops;
+}
+
+static zend_class_entry *php_http_client_curl_user_class_entry;
+
+zend_class_entry *php_http_client_curl_user_get_class_entry()
+{
+       return php_http_client_curl_user_class_entry;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_init, 0, 0, 1)
+       ZEND_ARG_TYPE_INFO(0, run, IS_CALLABLE, 0)
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_timer, 0, 0, 1)
+#if PHP_VERSION_ID >= 70000
+       ZEND_ARG_TYPE_INFO(0, timeout_ms, IS_LONG, 0)
+#else
+       ZEND_ARG_INFO(0, timeout_ms)
+#endif
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_socket, 0, 0, 2)
+       ZEND_ARG_INFO(0, socket)
+#if PHP_VERSION_ID >= 70000
+       ZEND_ARG_TYPE_INFO(0, action, IS_LONG, 0)
+#else
+       ZEND_ARG_INFO(0, action)
+#endif
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_once, 0, 0, 0)
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_wait, 0, 0, 0)
+#if PHP_VERSION_ID >= 70000
+       ZEND_ARG_TYPE_INFO(0, timeout_ms, IS_LONG, 0)
+#else
+       ZEND_ARG_INFO(0, timeout_ms)
+#endif
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_send, 0, 0, 0)
+ZEND_END_ARG_INFO();
+
+static zend_function_entry php_http_client_curl_user_methods[] = {
+       PHP_ABSTRACT_ME(HttpClientCurlUser, init, ai_init)
+       PHP_ABSTRACT_ME(HttpClientCurlUser, timer, ai_timer)
+       PHP_ABSTRACT_ME(HttpClientCurlUser, socket, ai_socket)
+       PHP_ABSTRACT_ME(HttpClientCurlUser, once, ai_once)
+       PHP_ABSTRACT_ME(HttpClientCurlUser, wait, ai_wait)
+       PHP_ABSTRACT_ME(HttpClientCulrUser, send, ai_send)
+       {0}
+};
+
+PHP_MINIT_FUNCTION(http_client_curl_user)
+{
+       zend_class_entry ce = {0};
+
+       INIT_NS_CLASS_ENTRY(ce, "http\\Client\\Curl", "User", php_http_client_curl_user_methods);
+       php_http_client_curl_user_class_entry = zend_register_internal_interface(&ce);
+
+       zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_NONE"), CURL_POLL_NONE);
+       zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_IN"), CURL_POLL_IN);
+       zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_OUT"), CURL_POLL_OUT);
+       zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_INOUT"), CURL_POLL_INOUT);
+       zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_REMOVE"), CURL_POLL_REMOVE);
+
+       return SUCCESS;
+}
+
+#endif /* PHP_HTTP_HAVE_CURL */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/src/php_http_client_curl_user.h b/src/php_http_client_curl_user.h
new file mode 100644 (file)
index 0000000..35f5d6f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: http                                                       |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_CLIENT_CURL_USER_H
+#define PHP_HTTP_CLIENT_CURL_USER_H
+
+#if PHP_HTTP_HAVE_CURL
+
+typedef struct php_http_client_curl_user_context {
+       php_http_client_t *client;
+       zval user;
+       zend_function closure;
+       php_http_object_method_t timer;
+       php_http_object_method_t socket;
+       php_http_object_method_t once;
+       php_http_object_method_t wait;
+       php_http_object_method_t send;
+} php_http_client_curl_user_context_t;
+
+PHP_HTTP_API zend_class_entry *php_http_client_curl_user_get_class_entry();
+PHP_HTTP_API php_http_client_curl_ops_t *php_http_client_curl_user_ops_get();
+PHP_MINIT_FUNCTION(http_client_curl_user);
+
+#endif
+
+#if 0
+<?php
+
+interface http\Client\Curl\User
+{
+       const POLL_NONE = 0;
+       const POLL_IN = 1;
+       const POLL_OUT = 2;
+       const POLL_INOUT = 3;
+       const POLL_REMOVE = 4;
+
+       /**
+        * Initialize the loop
+        *
+        * The callback should be run when:
+        *  - timeout occurs
+        *  - a watched socket needs action
+        *
+        * @param callable $run callback as function(http\Client $client, resource $socket = null, int $action = http\Client\Curl\User::POLL_NONE)
+        */
+       function init(callable $run);
+
+       /**
+        * Register a timeout watcher
+        * @param int $timeout_ms desired timeout with milliseconds resolution
+        */
+       function timer(int $timeout_ms);
+
+       /**
+        * (Un-)Register a socket watcher
+        * @param resource $socket the fd to watch
+        * @param int $poll http\Client\Curl\Loop::POLL_* constant
+        */
+       function socket($socket, int $poll);
+
+       /**
+        * Run the loop as long as it does not block
+        *
+        * Called by http\Client::once()
+        */
+       function once();
+
+       /**
+        * Wait/poll/select (block the loop) until events fire
+        *
+        * Called by http\Client::wait()
+        *
+        * @param int $timeout_ms block for maximal $timeout_ms milliseconds
+        */
+       function wait(int $timeout_ms = null);
+
+       /**
+        * Run the loop
+        *
+        * Called by http\Client::send() while there are unfinished requests and
+        * no exception has occurred
+        */
+       function send();
+}
+#endif
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
index c903624c703e890ce1d1a3f82e44ef15af0495c0..d907e8033d24354517cc127506f6c98c77aa9224 100644 (file)
@@ -46,10 +46,10 @@ php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **bo
 
        if (stream) {
                body->res = stream->res;
-               ++GC_REFCOUNT(body->res);
        } else {
                body->res = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff)->res;
        }
+       ++GC_REFCOUNT(body->res);
 
        if (body_ptr) {
                *body_ptr = body;
index 9fdad26ff0e909041b45e9aec74e18dd6d77b6df..cd09d3740f6dc8e293b72b3dbb742b096cca58ca 100644 (file)
 
 #include "php_http_api.h"
 
+#ifndef PHP_HTTP_DEBUG_NEG
+# define PHP_HTTP_DEBUG_NEG 0
+#endif
+
 static int php_http_negotiate_sort(const void *first, const void *second)
 {
        Bucket *b1 = (Bucket *) first, *b2 = (Bucket *) second;
@@ -22,7 +26,7 @@ static int php_http_negotiate_sort(const void *first, const void *second)
 
 #define M_PRI 5
 #define M_SEC 2
-#define M_ANY 1
+#define M_ANY -1
 #define M_NOT 0
 #define M_ALL ~0
 static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
@@ -46,7 +50,11 @@ static inline unsigned php_http_negotiate_match(const char *param_str, size_t pa
                        match += M_PRI;
                }
 
-               if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
+               if (param_sec && supported_sec
+               && ((*(param_sec + sep_len) == '*' || *(supported_sec + sep_len) == '*')
+                       || !strcasecmp(param_sec, supported_sec)
+                       )
+               ) {
                        match += M_SEC;
                }
 
@@ -57,7 +65,7 @@ static inline unsigned php_http_negotiate_match(const char *param_str, size_t pa
                        match += M_ANY;
                }
        }
-#if 0
+#if PHP_HTTP_DEBUG_NEG
        fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
 #endif
        return match;
@@ -65,8 +73,9 @@ static inline unsigned php_http_negotiate_match(const char *param_str, size_t pa
 static int php_http_negotiate_reduce(zval *p, int num_args, va_list args, zend_hash_key *hash_key)
 {
        unsigned best_match = 0;
+       double q = 0;
        php_http_arrkey_t key;
-       zval *value, *q = NULL;
+       zval *value;
        zend_string *supported = zval_get_string(p);
        HashTable *params = va_arg(args, HashTable *);
        HashTable *result = va_arg(args, HashTable *);
@@ -77,20 +86,25 @@ static int php_http_negotiate_reduce(zval *p, int num_args, va_list args, zend_h
        {
                unsigned match;
 
+#if PHP_HTTP_DEBUG_NEG
+                       fprintf(stderr, "match(%u) > best_match(%u) = %u (q=%f)\n", match, best_match, match>best_match, Z_DVAL_PP(val));
+#endif
                php_http_arrkey_stringify(&key, NULL);
                match = php_http_negotiate_match(key.key->val, key.key->len, supported->val, supported->len, sep_str, sep_len);
 
                if (match > best_match) {
                        best_match = match;
-                       q = value;
+                       q = Z_DVAL_P(value) - 0.1 / match;
                }
                php_http_arrkey_dtor(&key);
        }
        ZEND_HASH_FOREACH_END();
 
-       if (q && Z_DVAL_P(q) > 0) {
-               Z_TRY_ADDREF_P(q);
-               zend_hash_update(result, supported, q);
+       if (q > 0) {
+               zval tmp;
+
+               ZVAL_DOUBLE(&tmp, q);
+               zend_hash_update(result, supported, &tmp);
        }
 
        zend_string_release(supported);
@@ -112,6 +126,7 @@ HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable
                php_http_params_opts_default_get(&opts);
                opts.input.str = estrndup(value_str, value_len);
                opts.input.len = value_len;
+               opts.flags &= ~PHP_HTTP_PARAMS_RFC5987;
                php_http_params_parse(&params, &opts);
                efree(opts.input.str);
 
@@ -126,7 +141,7 @@ HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable
                        &&      (zq = zend_hash_str_find(Z_ARRVAL_P(arg), ZEND_STRL("q")))) {
                                q = zval_get_double(zq);
                        } else {
-                               q = 1.0 - ++i / 100.0;
+                               q = 1.0 - (((double) ++i) / 100.0);
                        }
 
 #if 0
@@ -142,7 +157,7 @@ HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable
                }
                ZEND_HASH_FOREACH_END();
 
-#if 0
+#if PHP_HTTP_DEBUG_NEG
                zend_print_zval_r(&arr, 1);
 #endif
 
index 8db5c353bd18c004ecc8bef617431577fafcb526..c9feccb6c56705e63088ded27a3a0f716acb7969 100644 (file)
@@ -253,11 +253,13 @@ static inline void sanitize_key(unsigned flags, const char *str, size_t len, zva
                return;
        }
 
-       eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
-       if (*eos == '*') {
-               *eos = '\0';
-               *rfc5987 = 1;
-               Z_STRLEN_P(zv) -= 1;
+       if (flags & PHP_HTTP_PARAMS_RFC5987) {
+               eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
+               if (*eos == '*') {
+                       *eos = '\0';
+                       *rfc5987 = 1;
+                       Z_STRLEN_P(zv) -= 1;
+               }
        }
 
        if (flags & PHP_HTTP_PARAMS_URLENCODED) {
@@ -554,6 +556,8 @@ static void push_param(HashTable *params, php_http_params_state_t *state, const
        if (state->val.str) {
                if (0 < (state->val.len = state->input.str - state->val.str)) {
                        sanitize_value(opts->flags, state->val.str, state->val.len, state->current.val, state->rfc5987);
+               } else {
+                       ZVAL_EMPTY_STRING(state->current.val);
                }
                state->rfc5987 = 0;
        } else if (state->arg.str) {
index ea84d8d1e6fed8a94a74113144218892a2849074..d45cd49923b21ce2b17746ddf0f56b84765b8fc8 100644 (file)
@@ -171,6 +171,25 @@ static int apply_querystring(zval *val)
        return ZEND_HASH_APPLY_KEEP;
 }
 
+static int apply_querystring_filter(zval *val)
+{
+       switch (Z_TYPE_P(val)) {
+       case IS_NULL:
+               return ZEND_HASH_APPLY_REMOVE;
+       case IS_ARRAY:
+       case IS_OBJECT:
+               zend_hash_apply(HASH_OF(val), apply_querystring_filter);
+               if (!zend_hash_num_elements(HASH_OF(val))) {
+                       return ZEND_HASH_APPLY_REMOVE;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
 ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size_t len)
 {
        ZEND_RESULT_CODE rv = FAILURE;
@@ -200,7 +219,7 @@ ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size
                zval_ptr_dtor(&arr);
        }
 
-       ZVAL_NULL(&opts.defval);
+       ZVAL_TRUE(&opts.defval);
 
        if (php_http_params_parse(ht, &opts)) {
                zend_hash_apply(ht, apply_querystring);
@@ -224,7 +243,9 @@ ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *o
        }
 
        /* modify qarray */
-       if (params) {
+       if (!params) {
+               zend_hash_apply(Z_ARRVAL_P(qarray), apply_querystring_filter);
+       } else {
                HashTable *ht;
                php_http_arrkey_t key;
                zval zv, *params_entry, *qarray_entry;
@@ -276,7 +297,7 @@ ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *o
                                                ZVAL_DUP(entry, qarray_entry);
                                                convert_to_array(entry);
                                                php_http_querystring_update(entry, params_entry, NULL);
-                                       } else if ((FAILURE == is_equal_function(&equal, qarray_entry, params_entry)) || Z_TYPE(equal) != IS_TRUE) {
+                                       } else if ((FAILURE == is_identical_function(&equal, qarray_entry, params_entry)) || Z_TYPE(equal) != IS_TRUE) {
                                                Z_TRY_ADDREF_P(params_entry);
                                                entry = params_entry;
                                        }
index 28d267cfdd891f18599e30d210791902b657d72d..5362c714aed0d5d4243a4bfc1f71e9eb1d40945a 100644 (file)
@@ -29,7 +29,7 @@ DONE
 --EXPECT--
 http://www.example.com/foobar?bar.baz=blah&utm_source=google&utm_campaign=somethingelse&blat
 
-http://www.example.com/foobar?bar.baz=blah&utm_source=changed&utm_campaign=somethingelse
+http://www.example.com/foobar?bar.baz=blah&utm_source=changed&utm_campaign=somethingelse&blat
 
 http://www.google.com/foobar?bar.baz=blah&utm_source=google&utm_campaign=somethingelse&blat
 
index c3ca9f96f3f655b9dff2f602187ed8f32b704955..884a3c60f23dac9063926e8f9add496e34b7325c 100644 (file)
@@ -12,6 +12,10 @@ include "helper/server.inc";
 
 echo "Test\n";
 
+function dump($response) {
+       echo $response->getHeader("X-Req"),"\n";
+}
+
 server("pipeline.inc", function($port, $stdin, $stdout, $stderr) {
        /* tell the server we're about to send 3 pipelined messages */
        fputs($stdin, "3\n");
@@ -20,37 +24,21 @@ server("pipeline.inc", function($port, $stdin, $stdout, $stderr) {
        $client->configure(array("pipelining" => true, "max_host_connections" => 0));
        
        /* this is just to let curl know the server may be capable of pipelining */
-       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port"));
+       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port"), "dump");
        $client->send();
        
-       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/1"));
-       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/2"));
-       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/3"));
+       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/1"), "dump");
+       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/2"), "dump");
+       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/3"), "dump");
        $client->send();
-       
-       while (($response = $client->getResponse())) {
-               echo $response;
-       }
 });
 
 ?>
 ===DONE===
 --EXPECT--
 Test
-HTTP/1.1 200 OK
-X-Req: /3
-Etag: ""
-X-Original-Transfer-Encoding: chunked
-HTTP/1.1 200 OK
-X-Req: /2
-Etag: ""
-X-Original-Transfer-Encoding: chunked
-HTTP/1.1 200 OK
-X-Req: /1
-Etag: ""
-X-Original-Transfer-Encoding: chunked
-HTTP/1.1 200 OK
-X-Req: /
-Etag: ""
-X-Original-Transfer-Encoding: chunked
+/
+/1
+/2
+/3
 ===DONE===
index 255de083bb3522a2df2751795c2e5943963802dd..e3f7f118812a86cc1faf11a4fd02615fbbfbc553 100644 (file)
@@ -18,7 +18,7 @@ nghttpd(function($port) {
        $client->setOptions([
                "protocol" => http\Client\Curl\HTTP_VERSION_2_0,
                "ssl" => [
-                       "cainfo" => __DIR__."/helper/http2.crt",
+                       "cafile" => __DIR__."/helper/http2.crt",
                ]
        ]);
        $client->enqueue(new http\Client\Request("GET", "https://localhost:$port"));
diff --git a/tests/client028.phpt b/tests/client028.phpt
new file mode 100644 (file)
index 0000000..27ab2b6
--- /dev/null
@@ -0,0 +1,141 @@
+--TEST--
+client curl user handler
+--SKIPIF--
+<?php 
+include "skipif.inc";
+skip_client_test();
+?> 
+--FILE--
+<?php 
+echo "Test\n";
+
+class UserHandler implements http\Client\Curl\User
+{
+       private $client;
+       private $run;
+       private $fds = [
+                       "R" => [],
+                       "W" => []
+       ];
+       private $R = [];
+       private $W = [];
+       private $timeout = 1000;
+       
+       function __construct(http\Client $client) {
+               $this->client = $client;
+       }
+       
+       function init(callable $run) {
+               $this->run = $run;
+       }
+       
+       function timer(int $timeout_ms) {
+               echo "T";
+               $this->timeout = $timeout_ms;
+       }
+       
+       function socket($socket, int $action) {
+               echo "S";
+               
+               switch ($action) {
+               case self::POLL_NONE:
+                       break;
+               case self::POLL_REMOVE:
+                       if (false !== ($r = array_search($socket, $this->fds["R"], true))) {
+                               echo "U";
+                               unset($this->fds["R"][$r]);
+                       }
+                       
+                       if (false !== ($w = array_search($socket, $this->fds["W"], true))) {
+                               echo "U";
+                               unset($this->fds["W"][$w]);
+                       }
+                       
+                       break;
+                       
+               default:
+                       if ($action & self::POLL_IN) {
+                               if (!in_array($socket, $this->fds["R"], true)) {
+                                       $this->fds["R"][] = $socket;
+                               }
+                       }
+                       if ($action & self::POLL_OUT) {
+                               if (!in_array($socket, $this->fds["W"], true)) {
+                                       $this->fds["W"][] = $socket;
+                               }
+                       }
+                       break;
+               }
+       }
+       
+       function once() {
+               echo "O";
+               
+               foreach ($this->W as $w) {
+                       call_user_func($this->run, $this->client, $w, self::POLL_OUT);
+               }
+               foreach ($this->R as $r) {
+                       call_user_func($this->run, $this->client, $r, self::POLL_IN);
+               }
+               return count($this->client);
+       }
+       
+       function wait(int $timeout_ms = null) {
+               echo "W";
+               
+               if ($timeout_ms === null) {
+                       $timeout_ms = $this->timeout;
+               }
+               $ts = floor($timeout_ms / 1000);
+               $tu = ($timeout_ms % 1000) * 1000;
+               
+               extract($this->fds);
+               
+               if (($wfds = count($R) + count($W))) {
+                       $nfds = stream_select($R, $W, $E, $ts, $tu);
+               } else {
+                       $nfds = 0;
+               }
+               $this->R = (array) $R;
+               $this->W = (array) $W;
+               
+               if ($nfds === false) {
+                       return false;
+               }
+               if (!$nfds) {
+                       if (!$wfds) {
+                               echo "S";
+                               time_nanosleep($ts, $tu*1000);
+                       }
+                       call_user_func($this->run, $this->client);
+               }
+                       
+               return true;
+       }
+       
+       function send() {
+               $this->wait();
+               $this->once();
+       }
+}
+
+
+include "helper/server.inc";
+
+server("proxy.inc", function($port) {
+       $client = new http\Client;
+       $client->configure([
+                       "use_eventloop" => new UserHandler($client)
+       ]);
+       $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/"), function($r) {
+               var_dump($r->getResponseCode());
+       });
+       $client->send();
+});
+
+?>
+===DONE===
+--EXPECTREGEX--
+Test
+T[WST]*(O[WST]*)+U+int\(200\)
+===DONE===
index 07c27cb17a7bc0ac1ec1e7553054d26715643a40..49a9609e49bfe319c449f5872e2147dac7a7a4e1 100644 (file)
@@ -9,12 +9,14 @@ include "skipif.inc";
 
 echo "Test\n";
 
+function r($v) { return round($v, 2); }
+
 $a = new http\Header("Accept", "text/html, text/plain;q=0.5, */*;q=0");
 var_dump("text/html" === $a->negotiate(array("text/plain","text/html")));
 var_dump("text/html" === $a->negotiate(array("text/plain","text/html"), $rs));
-var_dump(array("text/html"=>0.99, "text/plain"=>0.5) === $rs);
+var_dump(array("text/html"=>0.99, "text/plain"=>0.5) === array_map("r", $rs));
 var_dump("text/plain" === $a->negotiate(array("foo/bar", "text/plain"), $rs));
-var_dump(array("text/plain"=>0.5) === $rs);
+var_dump(array("text/plain"=>0.5) === array_map("r", $rs));
 var_dump("foo/bar" === $a->negotiate(array("foo/bar"), $rs));
 var_dump(array() === $rs);
 
index a6b16999c373b3835396edd44f17e5e4428e554f..6f3674ce9c1c764db8e81740a16a46e3153a8040 100644 (file)
@@ -23,8 +23,8 @@ serve(function($client) {
        /* pipelined messages */
        $req = array();
        for ($i=0; $i < $count; ++ $i) {
-               $req[] = new http\Message($client, false);
-               logger("Read request no. %d from client %d", $i+1, (int) $client);
+               $req[] = $m = new http\Message($client, false);
+               logger("Read request no. %d from client %d (%s)", $i+1, (int) $client, $m->getRequestUrl());
        }
        foreach ($req as $i => $msg) {
                respond($client, $msg);
index 0e553f50154e418a2e50531d1ceb91e0e5a9a670..ff1644bb0b432eec100bd20e17c06b5a0ac8c800 100644 (file)
@@ -11,43 +11,48 @@ HTTP_ACCEPT_LANGUAGE=de-DE,de-AT;q=0.9,en;q=0.8,fr;q=0
 CONTENT TYPE
 
 <?php
+
+function dump($ctr) {
+       print_r(array_map(function($v){return round($v,2);}, $ctr));
+}
+
 $ct = http\Env::negotiateContentType(array("text/html","text/xml","text/json"), $ctr);
-echo "$ct: "; print_r($ctr);
+echo "$ct: "; dump($ctr);
 $ct = http\Env::negotiateContentType(array("text/xml","text/json"), $ctr);
-echo "$ct: "; print_r($ctr);
+echo "$ct: "; dump($ctr);
 $ct = http\Env::negotiateContentType(array("text/json"), $ctr);
-echo "$ct: "; print_r($ctr);
+echo "$ct: "; dump($ctr);
 ?>
 
 CHARSET
 
 <?php
 $cs = http\Env::negotiateCharset(array("utf-8", "iso-8859-1", "iso-8859-15"), $csr);
-echo "$cs: "; print_r($csr);
+echo "$cs: "; dump($csr);
 $cs = http\Env::negotiateCharset(array("iso-8859-1", "iso-8859-15"), $csr);
-echo "$cs: "; print_r($csr);
+echo "$cs: "; dump($csr);
 $cs = http\Env::negotiateCharset(array("utf-16", "iso-8859-15", "iso-8859-2"), $csr);
-echo "$cs: "; print_r($csr);
+echo "$cs: "; dump($csr);
 ?>
 
 ENCODING
 
 <?php
 $ce = http\Env::negotiateEncoding(array("gzip", "deflate", "sdch"), $cer);
-echo "$ce: "; print_r($cer);
+echo "$ce: "; dump($cer);
 $ce = http\Env::negotiateEncoding(array("", "sdch"), $cer);
-echo "$ce: "; print_r($cer);
+echo "$ce: "; dump($cer);
 ?>
 
 LANGUAGE
 
 <?php
 $ln = http\Env::negotiateLanguage(array("de", "en", "fr"), $lnr);
-echo "$ln: "; print_r($lnr);
+echo "$ln: "; dump($lnr);
 $ln = http\Env::negotiateLanguage(array("de-DE", "de-AT", "en"), $lnr);
-echo "$ln: "; print_r($lnr);
+echo "$ln: "; dump($lnr);
 $ln = http\Env::negotiateLanguage(array("nl", "fr", "en"), $lnr);
-echo "$ln: "; print_r($lnr);
+echo "$ln: "; dump($lnr);
 ?>
 
 CUSTOM
@@ -55,7 +60,7 @@ CUSTOM
 <?php
 $cc = http\Env::negotiate("a, a.b;q=0.9, c.d;q=0, *.* ; q=0.1",
     array("a.x", "c.d", "c.e", "a.b"), ".", $ccr);
-echo "$cc: "; print_r($ccr);
+echo "$cc: "; dump($ccr);
 ?>
 DONE
 --EXPECT--
@@ -103,7 +108,7 @@ LANGUAGE
 
 de: Array
 (
-    [de] => 0.99
+    [de] => 0.97
     [en] => 0.8
 )
 de-DE: Array
@@ -122,7 +127,7 @@ CUSTOM
 a.b: Array
 (
     [a.b] => 0.9
-    [a.x] => 0.1
-    [c.e] => 0.1
+    [a.x] => 0.08
+    [c.e] => 0.08
 )
 DONE
index b3587e53e40a464bbb894aadd84d47a219afd2f5..95e8c54308916839507d8f78437227b587f95f23 100644 (file)
@@ -19,7 +19,7 @@ $p = current(http\Header::parse($link, "http\\Header"))->getParams(
        http\Params::DEF_PARAM_SEP,
        http\Params::DEF_ARG_SEP,
        http\Params::DEF_VAL_SEP,
-       http\Params::PARSE_RFC5988 | http\Params::PARSE_ESCAPED
+       http\Params::PARSE_RFC5987 | http\Params::PARSE_RFC5988 | http\Params::PARSE_ESCAPED
 );
 var_dump($p->params);
 var_dump((string)$p);
index 704ede23aaa2add3674268122ad592923a0daac0..de336a7fa60e1c8236922f788d8ae8ad54b0e795 100644 (file)
@@ -13,7 +13,7 @@ $u = "http://user:pass@www.example.com:8080/path/file.ext".
 var_dump($u === (string) new http\Url($u));
 
 $url = new http\Url($u, 
-       array("path" => "changed", "query" => "foo=&added=this"), 
+       array("path" => "changed", "query" => "bar&foo=&added=this"), 
        http\Url::JOIN_PATH |
        http\Url::JOIN_QUERY |
        http\Url::STRIP_AUTH |
@@ -40,6 +40,6 @@ NULL
 string(15) "www.example.com"
 int(8080)
 string(13) "/path/changed"
-string(38) "more%5B0%5D=1&more%5B1%5D=2&added=this"
+string(47) "foo=&more%5B0%5D=1&more%5B1%5D=2&bar&added=this"
 NULL
 DONE