ditch php_url
authorMichael Wallner <mike@php.net>
Fri, 7 Nov 2014 12:16:16 +0000 (13:16 +0100)
committerMichael Wallner <mike@php.net>
Fri, 7 Nov 2014 19:37:37 +0000 (20:37 +0100)
php_http_client_curl.c
php_http_client_request.c
php_http_info.c
php_http_info.h
php_http_message.c
php_http_url.c
php_http_url.h

index 48bd0de..b057216 100644 (file)
@@ -1598,7 +1598,7 @@ static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_
        if (storage->url) {
                pefree(storage->url, 1);
        }
-       storage->url = pestrdup(PHP_HTTP_INFO(msg).request.url, 1);
+       php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1);
        curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url);
 
        /* request method */
@@ -1784,38 +1784,29 @@ static void queue_dtor(php_http_client_enqueue_t *e)
        php_http_client_curl_handler_dtor(handler);
 }
 
-static php_resource_factory_t *create_rf(const char *url TSRMLS_DC)
+static php_resource_factory_t *create_rf(php_http_url_t *url TSRMLS_DC)
 {
-       php_url *purl;
+       php_persistent_handle_factory_t *pf;
        php_resource_factory_t *rf = NULL;
+       char *id_str = NULL;
+       size_t id_len;
 
-       if (!url || !*url) {
+       if (!url || (!url->host && !url->path)) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL");
                return NULL;
        }
 
-       purl = php_url_parse(url);
+       id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), url->port ? url->port : 80);
 
-       if (!purl) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse URL '%s'", url);
-               return NULL;
+       pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
+       if (pf) {
+               rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon);
        } else {
-               char *id_str = NULL;
-               size_t id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(purl->host), purl->port ? purl->port : 80);
-               php_persistent_handle_factory_t *pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC);
-
-               if (pf) {
-                       rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon);
-               }
-
-               php_url_free(purl);
-               efree(id_str);
-       }
-
-       if (!rf) {
                rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL);
        }
 
+       efree(id_str);
+
        return rf;
 }
 
index ea223be..eaf71da 100644 (file)
@@ -31,12 +31,12 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest___construct, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpClientRequest, __construct)
 {
-       char *meth_str = NULL, *url_str = NULL;
-       int meth_len = 0, url_len = 0;
-       zval *zheaders = NULL, *zbody = NULL;
+       char *meth_str = NULL;
+       int meth_len = 0;
+       zval *zheaders = NULL, *zbody = NULL, *zurl = NULL;
        php_http_message_object_t *obj;
 
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!a!O!", &meth_str, &meth_len, &url_str, &url_len, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return);
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!z!a!O!", &meth_str, &meth_len, &zurl, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return);
 
        obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 
@@ -52,8 +52,8 @@ static PHP_METHOD(HttpClientRequest, __construct)
        if (meth_str && meth_len) {
                PHP_HTTP_INFO(obj->message).request.method = estrndup(meth_str, meth_len);
        }
-       if (url_str && url_len) {
-               PHP_HTTP_INFO(obj->message).request.url = estrndup(url_str, url_len);
+       if (zurl) {
+               PHP_HTTP_INFO(obj->message).request.url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
        }
        if (zheaders) {
                array_copy(Z_ARRVAL_P(zheaders), &obj->message->hdrs);
@@ -113,8 +113,9 @@ static PHP_METHOD(HttpClientRequest, setQuery)
 {
        zval *qdata = NULL;
        php_http_message_object_t *obj;
-       php_url *old_url = NULL, new_url = {NULL};
+       php_http_url_t *old_url = NULL, new_url = {NULL};
        char empty[] = "";
+       unsigned flags = PHP_HTTP_URL_REPLACE;
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &qdata), invalid_arg, return);
 
@@ -137,18 +138,17 @@ static PHP_METHOD(HttpClientRequest, setQuery)
                new_url.query = Z_STRVAL(str);
                zval_dtor(&arr);
        } else {
-               new_url.query = &empty[0];
+               flags = PHP_HTTP_URL_STRIP_QUERY;
        }
 
        if (obj->message->http.info.request.url) {
-               old_url = php_url_parse(obj->message->http.info.request.url);
-               efree(obj->message->http.info.request.url);
+               old_url = obj->message->http.info.request.url;
        }
 
-       php_http_url(PHP_HTTP_URL_REPLACE, old_url, &new_url, NULL, &obj->message->http.info.request.url, NULL TSRMLS_CC);
+       obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, flags TSRMLS_CC);
 
        if (old_url) {
-               php_url_free(old_url);
+               php_http_url_free(&old_url);
        }
        if (new_url.query != &empty[0]) {
                STR_FREE(new_url.query);
@@ -166,16 +166,8 @@ static PHP_METHOD(HttpClientRequest, getQuery)
 
                PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj);
 
-               if (obj->message->http.info.request.url) {
-                       php_url *purl = php_url_parse(obj->message->http.info.request.url);
-
-                       if (purl) {
-                               if (purl->query) {
-                                       RETVAL_STRING(purl->query, 0);
-                                       purl->query = NULL;
-                               }
-                               php_url_free(purl);
-                       }
+               if (obj->message->http.info.request.url && obj->message->http.info.request.url->query) {
+                       RETVAL_STRING(obj->message->http.info.request.url->query, 1);
                }
        }
 }
@@ -187,7 +179,7 @@ static PHP_METHOD(HttpClientRequest, addQuery)
 {
        zval *qdata, arr, str;
        php_http_message_object_t *obj;
-       php_url *old_url = NULL, new_url = {NULL};
+       php_http_url_t *old_url = NULL, new_url = {NULL};
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata), invalid_arg, return);
 
@@ -208,14 +200,13 @@ static PHP_METHOD(HttpClientRequest, addQuery)
        zval_dtor(&arr);
 
        if (obj->message->http.info.request.url) {
-               old_url = php_url_parse(obj->message->http.info.request.url);
-               efree(obj->message->http.info.request.url);
+               old_url = obj->message->http.info.request.url;
        }
 
-       php_http_url(PHP_HTTP_URL_JOIN_QUERY, old_url, &new_url, NULL, &obj->message->http.info.request.url, NULL TSRMLS_CC);
+       obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
 
        if (old_url) {
-               php_url_free(old_url);
+               php_http_url_free(&old_url);
        }
        STR_FREE(new_url.query);
 
index 10ea0a3..2f1b746 100644 (file)
@@ -120,7 +120,7 @@ php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_head
                        while (' ' == *url) ++url;
                        while (' ' == *(http-1)) --http;
                        if (http > url) {
-                               PHP_HTTP_INFO(info).request.url = estrndup(url, http - url);
+                               PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC);
                        } else {
                                STR_SET(PHP_HTTP_INFO(info).request.method, NULL);
                                return NULL;
index afd747c..d31b505 100644 (file)
 #define PHP_HTTP_INFO_H
 
 #include "php_http_version.h"
+#include "php_http_url.h"
 
-#define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, eol) "%s %s HTTP/%u.%u" eol, \
+#define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, tmp, eol) "%s %s HTTP/%u.%u" eol, \
                                (_http_ptr)->info.request.method?(_http_ptr)->info.request.method:"UNKNOWN", \
-                               (_http_ptr)->info.request.url?(_http_ptr)->info.request.url:"/", \
+                               (_http_ptr)->info.request.url?php_http_url_to_string((_http_ptr)->info.request.url, &(tmp), NULL, 0):"/", \
                                (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
                                (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1
 
-#define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, eol) "HTTP/%u.%u %d%s%s" eol, \
+#define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, tmp, eol) "HTTP/%u.%u %d%s%s" eol, \
                                (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \
                                (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1, \
                                (_http_ptr)->info.response.code?(_http_ptr)->info.response.code:200, \
@@ -31,7 +32,7 @@
 typedef struct php_http_info_data {
        union {
                /* GET /foo/bar */
-               struct { char *method; char *url; } request;
+               struct { char *method; php_http_url_t *url; } request;
                /* 200 Ok */
                struct { unsigned code; char *status; } response;
        } info;
index c965317..1219d5d 100644 (file)
@@ -69,7 +69,7 @@ php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_h
                                message->http.info.request.method = estrdup(Z_STRVAL_P(sval));
                        }
                        if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1 TSRMLS_CC))) {
-                               message->http.info.request.url = estrdup(Z_STRVAL_P(sval));
+                               message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), ~0 TSRMLS_CC);
                        }
                        
                        php_http_env_get_request_headers(&message->hdrs TSRMLS_CC);
@@ -276,7 +276,7 @@ void php_http_message_set_info(php_http_message_t *message, php_http_info_t *inf
        message->http.version = info->http.version;
        switch (message->type) {
                case PHP_HTTP_REQUEST:
-                       STR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? estrdup(PHP_HTTP_INFO(info).request.url) : NULL);
+                       STR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? php_http_url_copy(PHP_HTTP_INFO(info).request.url, 0) : NULL);
                        STR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL);
                        break;
                
@@ -332,15 +332,18 @@ void php_http_message_update_headers(php_http_message_t *msg)
 
 static void message_headers(php_http_message_t *msg, php_http_buffer_t *str)
 {
+       char *tmp = NULL;
        TSRMLS_FETCH_FROM_CTX(msg->ts);
 
        switch (msg->type) {
                case PHP_HTTP_REQUEST:
-                       php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, PHP_HTTP_CRLF));
+                       php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
+                       STR_FREE(tmp);
                        break;
 
                case PHP_HTTP_RESPONSE:
-                       php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, PHP_HTTP_CRLF));
+                       php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF));
+                       STR_FREE(tmp);
                        break;
 
                default:
@@ -566,17 +569,18 @@ static void php_http_message_object_prophandler_set_request_method(php_http_mess
        }
 }
 static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
-       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url) {
-               RETVAL_STRING(obj->message->http.info.request.url, 1);
+       char *url_str;
+       size_t url_len;
+
+       if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url && php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0)) {
+               RETVAL_STRINGL(url_str, url_len, 0);
        } else {
                RETVAL_NULL();
        }
 }
 static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) {
        if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
-               zval *cpy = php_http_ztyp(IS_STRING, value);
-               STR_SET(obj->message->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
-               zval_ptr_dtor(&cpy);
+               STR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, ~0 TSRMLS_CC));
        }
 }
 static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) {
@@ -939,8 +943,8 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC)
        php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
        HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC);
        zval array, *parent, *body;
-       char *version;
-       int verlen;
+       char *ver_str, *url_str = NULL;
+       size_t ver_len, url_len = 0;
 
        PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
        INIT_PZVAL_ARRAY(&array, props);
@@ -964,15 +968,21 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC)
        } while(0)
 
        ASSOC_PROP(long, "type", obj->message->type);
-       verlen = spprintf(&version, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
-       ASSOC_STRINGL_EX("httpVersion", version, verlen, 0);
+       ver_len = spprintf(&ver_str, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
+       ASSOC_STRINGL_EX("httpVersion", ver_str, ver_len, 0);
 
        switch (obj->message->type) {
                case PHP_HTTP_REQUEST:
                        ASSOC_PROP(long, "responseCode", 0);
                        ASSOC_STRINGL("responseStatus", "", 0);
                        ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method));
-                       ASSOC_STRING("requestUrl", STR_PTR(obj->message->http.info.request.url));
+                       if (obj->message->http.info.request.url) {
+                               php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
+                               ASSOC_STRINGL_EX("requestUrl", url_str, url_len, 0);
+                       } else {
+                               ASSOC_STRINGL("requestUrl", "", 0);
+                       }
+
                        break;
 
                case PHP_HTTP_RESPONSE:
@@ -1320,16 +1330,19 @@ ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpMessage, getInfo)
 {
        if (SUCCESS == zend_parse_parameters_none()) {
+               char *tmp = NULL;
                php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 
                PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
 
                switch (obj->message->type) {
                        case PHP_HTTP_REQUEST:
-                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, ""));
+                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, tmp, ""));
+                               STR_FREE(tmp);
                                break;
                        case PHP_HTTP_RESPONSE:
-                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, ""));
+                               Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, tmp, ""));
+                               STR_FREE(tmp);
                                break;
                        default:
                                RETURN_NULL();
@@ -1564,7 +1577,11 @@ static PHP_METHOD(HttpMessage, getRequestUrl)
                }
 
                if (obj->message->http.info.request.url) {
-                       RETURN_STRING(obj->message->http.info.request.url, 1);
+                       char *url_str;
+                       size_t url_len;
+
+                       php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
+                       RETURN_STRINGL(url_str, url_len, 0);
                } else {
                        RETURN_EMPTY_STRING();
                }
@@ -1576,11 +1593,12 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestUrl, 0, 0, 1)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpMessage, setRequestUrl)
 {
-       char *url_str;
-       int url_len;
+       zval *zurl;
+       php_http_url_t *url;
        php_http_message_object_t *obj;
+       zend_error_handling zeh;
 
-       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &url_str, &url_len), invalid_arg, return);
+       php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zurl), invalid_arg, return);
 
        obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 
@@ -1591,12 +1609,17 @@ static PHP_METHOD(HttpMessage, setRequestUrl)
                return;
        }
 
-       if (url_len < 1) {
+       zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
+       url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (php_http_url_is_empty(url)) {
+               php_http_url_free(&url);
                php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string", NULL);
-               return;
+       } else {
+               STR_SET(obj->message->http.info.request.url, url);
        }
 
-       STR_SET(obj->message->http.info.request.url, estrndup(url_str, url_len));
        RETVAL_ZVAL(getThis(), 1, 0);
 }
 
index 5aeefa8..a375018 100644 (file)
@@ -131,7 +131,7 @@ void php_http_url(int flags, const php_url *old_url, const php_url *new_url, php
                *url_ptr = php_http_url_to_php_url(url);
        }
        if (url_str) {
-               php_http_url_to_string(url, url_str, url_len TSRMLS_CC);
+               php_http_url_to_string(url, url_str, url_len, 0);
        }
 
        php_http_url_free(&url);
@@ -272,7 +272,7 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
                url(buf)->host = &buf.data[buf.used];
                php_http_buffer_append(&buf, "localhost", sizeof("localhost"));
        }
-       
+
        if (!url(buf)->path) {
                url(buf)->path = &buf.data[buf.used];
                php_http_buffer_append(&buf, "/", sizeof("/"));
@@ -339,16 +339,17 @@ php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_u
        return url(buf);
 }
 
-void php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len TSRMLS_DC)
+char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent)
 {
        php_http_buffer_t buf;
 
-       php_http_buffer_init(&buf);
+       php_http_buffer_init_ex(&buf, PHP_HTTP_BUFFER_DEFAULT_SIZE, persistent ?
+                       PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
 
        if (url->scheme && *url->scheme) {
                php_http_buffer_appendl(&buf, url->scheme);
                php_http_buffer_appends(&buf, "://");
-       } else {
+       } else if ((url->user && *url->user) || (url->host && *url->host)) {
                php_http_buffer_appends(&buf, "//");
        }
 
@@ -363,8 +364,6 @@ void php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *u
 
        if (url->host && *url->host) {
                php_http_buffer_appendl(&buf, url->host);
-       } else {
-               php_http_buffer_appends(&buf, "localhost");
        }
 
        if (url->port) {
@@ -394,12 +393,32 @@ void php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *u
 
        if (url_str) {
                *url_str = buf.data;
-       } else {
-               php_http_buffer_dtor(&buf);
        }
+
+       return buf.data;
 }
 
-php_http_url_t *php_http_url_from_struct(HashTable *ht TSRMLS_DC)
+php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC)
+{
+       zval *zcpy;
+       php_http_url_t *purl;
+
+       switch (Z_TYPE_P(value)) {
+       case IS_ARRAY:
+       case IS_OBJECT:
+               purl = php_http_url_from_struct(HASH_OF(value));
+               break;
+
+       default:
+               zcpy = php_http_ztyp(IS_STRING, value);
+               purl = php_http_url_parse(Z_STRVAL_P(zcpy), Z_STRLEN_P(zcpy), flags TSRMLS_CC);
+               zval_ptr_dtor(&zcpy);
+       }
+
+       return purl;
+}
+
+php_http_url_t *php_http_url_from_struct(HashTable *ht)
 {
        zval **e;
        php_http_buffer_t buf;
@@ -562,6 +581,43 @@ void php_http_url_free(php_http_url_t **url)
        }
 }
 
+php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent)
+{
+       php_http_url_t *cpy;
+       const char *end = NULL, *url_ptr = (const char *) url;
+       char *cpy_ptr;
+
+       end = MAX(url->scheme, end);
+       end = MAX(url->pass, end);
+       end = MAX(url->user, end);
+       end = MAX(url->host, end);
+       end = MAX(url->path, end);
+       end = MAX(url->query, end);
+       end = MAX(url->fragment, end);
+
+       if (end) {
+               end += strlen(end) + 1;
+               cpy_ptr = pecalloc(1, end - url_ptr, persistent);
+               cpy = (php_http_url_t *) cpy_ptr;
+
+               memcpy(cpy_ptr + sizeof(*cpy), url_ptr + sizeof(*url), end - url_ptr - sizeof(*url));
+
+               cpy->scheme = url->scheme ? cpy_ptr + (url->scheme - url_ptr) : NULL;
+               cpy->pass = url->pass ? cpy_ptr + (url->pass - url_ptr) : NULL;
+               cpy->user = url->user ? cpy_ptr + (url->user - url_ptr) : NULL;
+               cpy->host = url->host ? cpy_ptr + (url->host - url_ptr) : NULL;
+               cpy->path = url->path ? cpy_ptr + (url->path - url_ptr) : NULL;
+               cpy->query = url->query ? cpy_ptr + (url->query - url_ptr) : NULL;
+               cpy->fragment = url->fragment ? cpy_ptr + (url->fragment - url_ptr) : NULL;
+       } else {
+               cpy = ecalloc(1, sizeof(*url));
+       }
+
+       cpy->port = url->port;
+
+       return cpy;
+}
+
 static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
 {
        unsigned wchar;
@@ -933,14 +989,7 @@ static const char *parse_path(struct parse_state *state)
                switch (*state->ptr) {
                case '#':
                case '?':
-               case '\0':
-                       /* did we have any path component ? */
-                       if (tmp != state->ptr) {
-                               state->buffer[state->offset++] = 0;
-                       } else {
-                               state->url.path = NULL;
-                       }
-                       return state->ptr;
+                       goto done;
 
                case '%':
                        if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
@@ -979,9 +1028,16 @@ static const char *parse_path(struct parse_state *state)
                        }
                        state->ptr += mb - 1;
                }
-       } while (++state->ptr <= state->end);
+       } while (++state->ptr < state->end);
 
-       return NULL;
+       done:
+       /* did we have any path component ? */
+       if (tmp != state->ptr) {
+               state->buffer[state->offset++] = 0;
+       } else {
+               state->url.path = NULL;
+       }
+       return state->ptr;
 }
 
 static const char *parse_query(struct parse_state *state)
@@ -1002,9 +1058,7 @@ static const char *parse_query(struct parse_state *state)
        do {
                switch (*state->ptr) {
                case '#':
-               case '\0':
-                       state->buffer[state->offset++] = 0;
-                       return state->ptr;
+                       goto done;
 
                case '%':
                        if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
@@ -1053,9 +1107,11 @@ static const char *parse_query(struct parse_state *state)
                        }
                        state->ptr += mb - 1;
                }
-       } while (++state->ptr <= state->end);
+       } while (++state->ptr < state->end);
 
-       return NULL;
+       done:
+       state->buffer[state->offset++] = 0;
+       return state->ptr;
 }
 
 static const char *parse_fragment(struct parse_state *state)
@@ -1075,10 +1131,6 @@ static const char *parse_fragment(struct parse_state *state)
 
        do {
                switch (*state->ptr) {
-               case '\0':
-                       state->buffer[state->offset++] = 0;
-                       return state->ptr;
-
                case '%':
                        if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) {
                                php_error_docref(NULL TSRMLS_CC, E_WARNING,
@@ -1116,9 +1168,10 @@ static const char *parse_fragment(struct parse_state *state)
                        }
                        state->ptr += mb - 1;
                }
-       } while (++state->ptr <= state->end);
+       } while (++state->ptr < state->end);
 
-       return NULL;
+       state->buffer[state->offset++] = 0;
+       return state->ptr;
 }
 
 static const char *parse_hier(struct parse_state *state)
@@ -1235,38 +1288,14 @@ PHP_METHOD(HttpUrl, __construct)
                php_http_url_t *res_purl, *new_purl = NULL, *old_purl = NULL;
 
                if (new_url) {
-                       switch (Z_TYPE_P(new_url)) {
-                               case IS_OBJECT:
-                               case IS_ARRAY:
-                                       new_purl = php_http_url_from_struct(HASH_OF(new_url) TSRMLS_CC);
-                                       break;
-                               default: {
-                                       zval *cpy = php_http_ztyp(IS_STRING, new_url);
-
-                                       new_purl = php_http_url_parse(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags TSRMLS_CC);
-                                       zval_ptr_dtor(&cpy);
-                                       break;
-                               }
-                       }
+                       new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
                        if (!new_purl) {
                                zend_restore_error_handling(&zeh TSRMLS_CC);
                                return;
                        }
                }
                if (old_url) {
-                       switch (Z_TYPE_P(old_url)) {
-                               case IS_OBJECT:
-                               case IS_ARRAY:
-                                       old_purl = php_http_url_from_struct(HASH_OF(old_url) TSRMLS_CC);
-                                       break;
-                               default: {
-                                       zval *cpy = php_http_ztyp(IS_STRING, old_url);
-
-                                       old_purl = php_http_url_parse(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags TSRMLS_CC);
-                                       zval_ptr_dtor(&cpy);
-                                       break;
-                               }
-                       }
+                       old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC);
                        if (!old_purl) {
                                if (new_purl) {
                                        php_http_url_free(&new_purl);
@@ -1307,26 +1336,14 @@ PHP_METHOD(HttpUrl, mod)
                php_http_url_t *new_purl = NULL, *old_purl = NULL;
 
                if (new_url) {
-                       switch (Z_TYPE_P(new_url)) {
-                               case IS_OBJECT:
-                               case IS_ARRAY:
-                                       new_purl = php_http_url_from_struct(HASH_OF(new_url) TSRMLS_CC);
-                                       break;
-                               default: {
-                                       zval *cpy = php_http_ztyp(IS_STRING, new_url);
-
-                                       new_purl = php_http_url_parse(Z_STRVAL_P(new_url), Z_STRLEN_P(new_url), flags TSRMLS_CC);
-                                       zval_ptr_dtor(&cpy);
-                                       break;
-                               }
-                       }
+                       new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC);
                        if (!new_purl) {
                                zend_restore_error_handling(&zeh TSRMLS_CC);
                                return;
                        }
                }
 
-               if ((old_purl = php_http_url_from_struct(HASH_OF(getThis()) TSRMLS_CC))) {
+               if ((old_purl = php_http_url_from_struct(HASH_OF(getThis())))) {
                        php_http_url_t *res_purl;
 
                        ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0);
@@ -1351,11 +1368,11 @@ PHP_METHOD(HttpUrl, toString)
        if (SUCCESS == zend_parse_parameters_none()) {
                php_http_url_t *purl;
 
-               if ((purl = php_http_url_from_struct(HASH_OF(getThis()) TSRMLS_CC))) {
+               if ((purl = php_http_url_from_struct(HASH_OF(getThis())))) {
                        char *str;
                        size_t len;
 
-                       php_http_url_to_string(purl, &str, &len TSRMLS_CC);
+                       php_http_url_to_string(purl, &str, &len, 0);
                        php_http_url_free(&purl);
                        RETURN_STRINGL(str, len, 0);
                }
@@ -1374,7 +1391,7 @@ PHP_METHOD(HttpUrl, toArray)
        }
 
        /* strip any non-URL properties */
-       purl = php_http_url_from_struct(HASH_OF(getThis()) TSRMLS_CC);
+       purl = php_http_url_from_struct(HASH_OF(getThis()));
        php_http_url_to_struct(purl, return_value TSRMLS_CC);
        php_http_url_free(&purl);
 }
index a6dda53..fb91932 100644 (file)
@@ -57,12 +57,17 @@ typedef struct php_http_url {
 } php_http_url_t;
 
 PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC);
-PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
-
 /* deprecated */
 PHP_HTTP_API void php_http_url(int flags, const php_url *old_url, const php_url *new_url, php_url **url_ptr, char **url_str, size_t *url_len TSRMLS_DC);
 /* use this instead */
 PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC);
+PHP_HTTP_API php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent);
+PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht);
+PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC);
+PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
+PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent);
+PHP_HTTP_API void php_http_url_free(php_http_url_t **url);
+
 
 PHP_HTTP_API STATUS php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC);
 PHP_HTTP_API STATUS php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC);
@@ -90,9 +95,9 @@ static inline php_url *php_http_url_to_php_url(php_http_url_t *url)
        return purl;
 }
 
-PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht TSRMLS_DC);
-PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC);
-PHP_HTTP_API void php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len TSRMLS_DC);
+static inline zend_bool php_http_url_is_empty(const php_http_url_t *url) {
+       return !(url->scheme || url->pass || url->user || url->host || url->port ||     url->path || url->query || url->fragment);
+}
 
 PHP_HTTP_API zend_class_entry *php_http_url_class_entry;
 PHP_MINIT_FUNCTION(http_url);