[ --with-http-libcurl-dir[=DIR] HTTP: where to find libcurl], $PHP_HTTP, $PHP_HTTP)
PHP_ARG_WITH([http-libevent-dir], [],
[ --with-http-libevent-dir[=DIR] HTTP: where to find libevent], $PHP_HTTP_LIBCURL_DIR, "")
+PHP_ARG_WITH([http-libidn-dir], [],
+[ --with-http-libidn-dir=[=DIR] HTTP: where to find libidn], $PHP_HTTP_LIBCURL_DIR, "")
if test "$PHP_HTTP" != "no"; then
dnl STDC
dnl ----
AC_TYPE_OFF_T
+ AC_TYPE_MBSTATE_T
dnl getdomainname() is declared in netdb.h on some platforms: AIX, OSF
- AC_CHECK_HEADERS([netdb.h unistd.h])
+ AC_CHECK_HEADERS([netdb.h unistd.h wchar.h wctype.h arpa/inet.h])
PHP_CHECK_FUNC(gethostname, nsl)
PHP_CHECK_FUNC(getdomainname, nsl)
+ PHP_CHECK_FUNC(mbrtowc)
+ PHP_CHECK_FUNC(mbtowc)
+ PHP_CHECK_FUNC(iswalnum)
+ PHP_CHECK_FUNC(inet_pton)
+
+dnl ----
+dnl IDN
+dnl ----
+
+ AC_MSG_CHECKING([for idna.h])
+ IDNA_DIR=
+ for i in "$PHP_HTTP_LIBIDN_DIR" "$IDN_DIR" /usr/local /usr /opt; do
+ if test -f "$i/include/idna.h"; then
+ IDNA_DIR=$i
+ break;
+ fi
+ done
+ if test "x$IDNA_DIR" = "x"; then
+ AC_MSG_RESULT([not found])
+ else
+ AC_MSG_RESULT([found in $IDNA_DIR])
+ AC_DEFINE([PHP_HTTP_HAVE_IDN], [1], [Have libidn support])
+ PHP_ADD_INCLUDE($IDNA_DIR/include)
+ PHP_ADD_LIBRARY_WITH_PATH(idn, $IDNA_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD)
+ fi
dnl ----
dnl ZLIB
--- /dev/null
+#!/usr/bin/env php
+<?php
+
+error_reporting(E_ALL);
+set_error_handler(function($c, $e, $f, $l) {
+ throw new Exception("$e in $f on line $l");
+});
+
+$i18n = $argc >= 2 ? $argv[1] : "/usr/share/i18n/locales/i18n";
+
+$f = fopen($i18n, "r");
+$c = false;
+$a = false;
+
+ob_start(null, 0xffff);
+while (!feof($f)) {
+ $line = fgets($f);
+ if (!$c && $line !== "LC_CTYPE\n") {
+ continue;
+ }
+ $c = true;
+ if ($line === "END LC_CTYPE\n") {
+ break;
+ }
+ switch($line{0}) {
+ case "%":
+ break;
+ case "\n":
+ if ($a) {
+ break 2;
+ }
+ break;
+ case " ":
+ if ($a) {
+ foreach (explode(";", trim($line, "\n/ ;")) as $ranges) {
+ $range = explode("..", $ranges);
+ $step = 0;
+ $end = 0;
+ switch (count($range)) {
+ case 3:
+ list($sstart, $sstep, $send) = $range;
+ sscanf($sstart, "<U%X>", $start);
+ sscanf($sstep, "(%d)", $step);
+ sscanf($send, "<U%X>", $end);
+
+ break;
+ case 2:
+ list($sstart, $send) = $range;
+ $step = 1;
+ sscanf($sstart, "<U%X>", $start);
+ sscanf($send, "<U%X>", $end);
+ break;
+ case 1:
+ list($sstart) = $range;
+ sscanf($sstart, "<U%X>", $start);
+ break;
+ }
+ print "\t{";
+ if ($start >= 0xffff) {
+ printf("0x%08X, ", $start);
+ if ($end) {
+ printf("0x%08X, ", $end);
+ } else {
+ print(" 0, ");
+ }
+ } else {
+ printf(" 0x%04X, ", $start);
+ if ($end) {
+ printf(" 0x%04X, ", $end);
+ } else {
+ print(" 0, ");
+ }
+ }
+ printf("%d},\n", $step);
+ }
+ }
+ break;
+ default:
+ if ($a) {
+ break 2;
+ } elseif ($line === "alpha /\n") {
+ $a = true;
+ }
+ break;
+ }
+}
+
+file_put_contents("php_http_utf8.h",
+ preg_replace('/(\/\* BEGIN::UTF8TABLE \*\/\n).*(\n\s*\/\* END::UTF8TABLE \*\/)/s', '$1'. ob_get_contents() .'$2',
+ file_get_contents("php_http_utf8.h")));
<email>mike@php.net</email>
<active>yes</active>
</lead>
- <date>2014-11-06</date>
+ <date>2014-08-19</date>
<version>
- <release>2.1.5dev</release>
- <api>2.1.0</api>
+ <release>2.2.0dev</release>
+ <api>2.2.0</api>
</version>
<stability>
- <release>stable</release>
+ <release>beta</release>
<api>stable</api>
</stability>
<license>BSD, revised</license>
<notes><![CDATA[
-* Fixed bug #68353 (QsoSSL support removed in libcurl 7.39)
-* Fixed bug #68149 (duplicate content-length with libcurl < 7.23)
-* Fixed bug #66891 (Unexpected HTTP 401 after NTLM authentication)
+- var_dump(http\Message) no longer automatically creates an empty body
++ Added http\Message\Parser class
++ Made http\Client::once() and http\Client::wait() available when using events
++ Added http\Url::parse() method
++ Added http\Url::PARSE_MBLOC, http\Url::PARSE_MBUTF8, http\Url::PARSE_TOIDN and http\Url::PARSE_TOPCT constants
]]></notes>
<contents>
<dir name="/">
<file role="src" name="php_http_strlist.h"/>
<file role="src" name="php_http_url.c"/>
<file role="src" name="php_http_url.h"/>
+ <file role="src" name="php_http_utf8.h"/>
<file role="src" name="php_http_version.c"/>
<file role="src" name="php_http_version.h"/>
</dir>
<file role="test" name="bug61444.phpt"/>
<file role="test" name="bug66388.phpt"/>
- <file role="test" name="bug66891.phpt"/>
<file role="test" name="bug67932.phpt"/>
<file role="test" name="client001.phpt"/>
<file role="test" name="client002.phpt"/>
<file role="test" name="client013.phpt"/>
<file role="test" name="client014.phpt"/>
<file role="test" name="client015.phpt"/>
+ <file role="test" name="client016.phpt"/>
<file role="test" name="clientrequest001.phpt"/>
<file role="test" name="clientrequest002.phpt"/>
<file role="test" name="clientrequest003.phpt"/>
<file role="test" name="messagebody008.phpt"/>
<file role="test" name="messagebody009.phpt"/>
<file role="test" name="messagebody010.phpt"/>
+ <file role="test" name="messageparser001.phpt"/>
+ <file role="test" name="messageparser002.phpt"/>
<file role="test" name="negotiate001.phpt"/>
<file role="test" name="params001.phpt"/>
<file role="test" name="params002.phpt"/>
<file role="test" name="querystring001.phpt"/>
<file role="test" name="querystring002.phpt"/>
<file role="test" name="serialize001.phpt"/>
+ <file role="test" name="url001.phpt"/>
<file role="test" name="url002.phpt"/>
<file role="test" name="url003.phpt"/>
<file role="test" name="url004.phpt"/>
<file role="test" name="url005.phpt"/>
- <file role="test" name="url001.phpt"/>
+ <file role="test" name="urlparser001.phpt"/>
+ <file role="test" name="urlparser002.phpt"/>
+ <file role="test" name="urlparser003.phpt"/>
+ <file role="test" name="urlparser004.phpt"/>
+ <file role="test" name="urlparser005.phpt"/>
+ <file role="test" name="urlparser006.phpt"/>
+ <file role="test" name="urlparser007.phpt"/>
+ <file role="test" name="urlparser008.phpt"/>
+ <file role="test" name="urlparser009.phpt"/>
+ <file role="test" name="urlparser010.phpt"/>
<file role="test" name="version001.phpt"/>
</dir>
</dir>
<optional>
<extension><name>hash</name></extension>
<extension><name>iconv</name></extension>
+ <extension><name>json</name></extension>
</optional>
</dependencies>
<providesextension>http</providesextension>
|| SUCCESS != PHP_MINIT_CALL(http_filter)
|| SUCCESS != PHP_MINIT_CALL(http_header)
|| SUCCESS != PHP_MINIT_CALL(http_message)
+ || SUCCESS != PHP_MINIT_CALL(http_message_parser)
|| SUCCESS != PHP_MINIT_CALL(http_message_body)
|| SUCCESS != PHP_MINIT_CALL(http_querystring)
|| SUCCESS != PHP_MINIT_CALL(http_client)
#ifndef PHP_EXT_HTTP_H
#define PHP_EXT_HTTP_H
-#define PHP_PECL_HTTP_VERSION "2.1.5dev"
+#define PHP_PECL_HTTP_VERSION "2.2.0dev"
extern zend_module_entry http_module_entry;
#define phpext_http_ptr &http_module_entry
# endif
#endif
+#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTYPE_H) && defined(HAVE_ISWALNUM) && (defined(HAVE_MBRTOWC) || defined(HAVE_MBTOWC))
+# define PHP_HTTP_HAVE_WCHAR 1
+#endif
+
#include <ctype.h>
#define PHP_HTTP_IS_CTYPE(type, c) is##type((int) (unsigned char) (c))
#define PHP_HTTP_TO_CTYPE(type, c) to##type((int) (unsigned char) (c))
} else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) {
h->progress.info = "timeout";
} else {
-#if PHP_DEBUG
+#if 0
h->progress.info = data;
data[length - 1] = '\0';
#endif
if (!event_initialized(curl->timeout)) {
event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
- } else if (event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
- event_del(curl->timeout);
}
timeout.tv_sec = timeout_ms / 1000;
return FAILURE;
}
} else {
- if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) PHP_HTTP_G->env.request.time + Z_LVAL_P(val))) {
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time(TSRMLS_C) + Z_LVAL_P(val))) {
return FAILURE;
}
}
}
}
+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, &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
#if PHP_HTTP_HAVE_EVENT
if (curl->useevents) {
- TSRMLS_FETCH_FROM_CTX(h->ts);
+ 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);
+ }
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "not implemented");
- return FAILURE;
+ event_base_loop(curl->evbase, EVLOOP_ONCE);
+
+ return SUCCESS;
}
#endif
if (custom_timeout && timerisset(custom_timeout)) {
timeout = *custom_timeout;
} else {
- long max_tout = 1000;
-
- if ((CURLM_OK == curl_multi_timeout(curl->handle, &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_client_curl_get_timeout(curl, 1000, &timeout);
}
if (MAX == -1) {
#if PHP_HTTP_HAVE_EVENT
if (curl->useevents) {
- TSRMLS_FETCH_FROM_CTX(h->ts);
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "not implemented");
- return FAILURE;
- }
+ event_base_loop(curl->evbase, EVLOOP_NONBLOCK);
+ } else
#endif
-
while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle, &curl->unfinished));
php_http_curlm_responsehandler(h);
PHP_RINIT_FUNCTION(http_env)
{
- PHP_HTTP_G->env.request.time = sapi_get_request_time(TSRMLS_C);
-
/* populate form data on non-POST requests */
if (SG(request_info).request_method && strcasecmp(SG(request_info).request_method, "POST") && SG(request_info).content_type && *SG(request_info).content_type) {
uint ct_len = strlen(SG(request_info).content_type);
char *etag_mode;
struct {
- time_t time;
HashTable *headers;
php_http_message_body_t *body;
} request;
{
zval *headers;
php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
- php_http_message_t *msg = obj->message;
HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC);
zval array, *parent, *body;
char *version;
int verlen;
PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
-
INIT_PZVAL_ARRAY(&array, props);
#define ASSOC_PROP(ptype, n, val) \
verlen = spprintf(&version, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
ASSOC_STRINGL_EX("httpVersion", version, verlen, 0);
- switch (msg->type) {
+ switch (obj->message->type) {
case PHP_HTTP_REQUEST:
ASSOC_PROP(long, "responseCode", 0);
ASSOC_STRINGL("responseStatus", "", 0);
- ASSOC_STRING("requestMethod", STR_PTR(msg->http.info.request.method));
- ASSOC_STRING("requestUrl", STR_PTR(msg->http.info.request.url));
+ ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method));
+ ASSOC_STRING("requestUrl", STR_PTR(obj->message->http.info.request.url));
break;
case PHP_HTTP_RESPONSE:
- ASSOC_PROP(long, "responseCode", msg->http.info.response.code);
- ASSOC_STRING("responseStatus", STR_PTR(msg->http.info.response.status));
+ ASSOC_PROP(long, "responseCode", obj->message->http.info.response.code);
+ ASSOC_STRING("responseStatus", STR_PTR(obj->message->http.info.response.status));
ASSOC_STRINGL("requestMethod", "", 0);
ASSOC_STRINGL("requestUrl", "", 0);
break;
MAKE_STD_ZVAL(headers);
array_init(headers);
- zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+ zend_hash_copy(Z_ARRVAL_P(headers), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
ASSOC_PROP(zval, "headers", headers);
MAKE_STD_ZVAL(body);
- if (!obj->body) {
- php_http_new(NULL, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, (void *) php_http_message_body_init(&obj->message->body, NULL TSRMLS_CC), (void *) &obj->body TSRMLS_CC);
+ if (obj->body) {
+ ZVAL_OBJVAL(body, obj->body->zv, 1);
+ } else {
+ ZVAL_NULL(body);
}
- ZVAL_OBJVAL(body, obj->body->zv, 1);
ASSOC_PROP(zval, "body", body);
MAKE_STD_ZVAL(parent);
- if (msg->parent) {
+ if (obj->message->parent) {
ZVAL_OBJVAL(parent, obj->parent->zv, 1);
} else {
ZVAL_NULL(parent);
if (s && php_http_message_parser_init(&p TSRMLS_CC)) {
unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0);
+ php_http_buffer_t buf;
- if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, s, flags, &msg)) {
+ php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC);
+ if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, &buf, s, flags, &msg)) {
if (!EG(exception)) {
php_http_throw(bad_message, "Could not parse message from stream", NULL);
}
}
-
+ php_http_buffer_dtor(&buf);
php_http_message_parser_dtor(&p);
}
{
php_http_header_parser_dtor(&parser->header);
zend_ptr_stack_destroy(&parser->stack);
+ php_http_message_free(&parser->message);
if (parser->dechunk) {
php_http_encoding_stream_free(&parser->dechunk);
}
}
}
-php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, unsigned flags, php_http_message_t **message)
+php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message)
{
- php_http_buffer_t buf;
php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
TSRMLS_FETCH_FROM_CTX(parser->ts);
- php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC);
-
+ if (!buf->data) {
+ php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
+ }
while (!php_stream_eof(s)) {
- size_t len = 0;
+ size_t justread = 0;
#if DBG_PARSER
fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
#endif
case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
/* read line */
- php_stream_get_line(s, buf.data + buf.used, buf.free, &len);
- php_http_buffer_account(&buf, len);
+ php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
+ php_http_buffer_account(buf, justread);
break;
case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
/* read all */
- php_http_buffer_account(&buf, php_stream_read(s, buf.data + buf.used, buf.free));
+ justread = php_stream_read(s, buf->data + buf->used, buf->free);
+ php_http_buffer_account(buf, justread);
break;
case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
/* read body_length */
- php_http_buffer_account(&buf, php_stream_read(s, buf.data + buf.used, MIN(buf.free, parser->body_length)));
+ justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length));
+ php_http_buffer_account(buf, justread);
break;
case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
/* duh, this is very naive */
- if (len) {
- size_t read = php_stream_read(s, buf.data + buf.used, MIN(len, buf.free));
+ if (parser->body_length) {
+ justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free));
- php_http_buffer_account(&buf, read);
+ php_http_buffer_account(buf, justread);
- len -= read;
+ parser->body_length -= justread;
} else {
- php_http_buffer_resize(&buf, 24);
- php_stream_get_line(s, buf.data, buf.free, &len);
- php_http_buffer_account(&buf, len);
+ php_http_buffer_resize(buf, 24);
+ php_stream_get_line(s, buf->data, buf->free, &justread);
+ php_http_buffer_account(buf, justread);
- len = strtoul(buf.data + buf.used - len, NULL, 16);
+ parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16);
}
break;
case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
- php_http_buffer_dtor(&buf);
return php_http_message_parser_state_is(parser);
}
- state = php_http_message_parser_parse(parser, &buf, flags, message);
+ if (justread) {
+ state = php_http_message_parser_parse(parser, buf, flags, message);
+ } else {
+ return state;
+ }
}
- php_http_buffer_dtor(&buf);
return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
}
return php_http_message_parser_state_is(parser);
}
+zend_class_entry *php_http_message_parser_class_entry;
+static zend_object_handlers php_http_message_parser_object_handlers;
+
+zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+ return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
+}
+
+zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC)
+{
+ php_http_message_parser_object_t *o;
+
+ o = ecalloc(1, sizeof(php_http_message_parser_object_t));
+ zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
+ object_properties_init((zend_object *) o, ce);
+
+ if (ptr) {
+ *ptr = o;
+ }
+
+ if (parser) {
+ o->parser = parser;
+ } else {
+ o->parser = php_http_message_parser_init(NULL TSRMLS_CC);
+ }
+ o->buffer = php_http_buffer_new();
+
+ o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC);
+ o->zv.handlers = &php_http_message_parser_object_handlers;
+
+ return o->zv;
+}
+
+void php_http_message_parser_object_free(void *object TSRMLS_DC)
+{
+ php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object;
+
+ if (o->parser) {
+ php_http_message_parser_free(&o->parser);
+ }
+ if (o->buffer) {
+ php_http_buffer_free(&o->buffer);
+ }
+ zend_object_std_dtor((zend_object *) o TSRMLS_CC);
+ efree(o);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessageParser, getState)
+{
+ php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ zend_parse_parameters_none();
+ /* always return the real state */
+ RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser));
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
+ ZEND_ARG_INFO(0, data)
+ ZEND_ARG_INFO(0, flags)
+ ZEND_ARG_INFO(1, message)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessageParser, parse)
+{
+ php_http_message_parser_object_t *parser_obj;
+ zval *zmsg;
+ char *data_str;
+ int data_len;
+ long flags;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
+
+ parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+ php_http_buffer_append(parser_obj->buffer, data_str, data_len);
+ RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message));
+
+ zval_dtor(zmsg);
+ if (parser_obj->parser->message) {
+ ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3)
+ ZEND_ARG_INFO(0, stream)
+ ZEND_ARG_INFO(0, flags)
+ ZEND_ARG_INFO(1, message)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(HttpMessageParser, stream)
+{
+ php_http_message_parser_object_t *parser_obj;
+ zend_error_handling zeh;
+ zval *zmsg, *zstream;
+ php_stream *s;
+ long flags;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
+
+ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
+ php_stream_from_zval(s, &zstream);
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+ RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message));
+
+ zval_dtor(zmsg);
+ if (parser_obj->parser->message) {
+ ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
+ }
+}
+
+static zend_function_entry php_http_message_parser_methods[] = {
+ PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC)
+ {NULL, NULL, NULL}
+};
+
+PHP_MINIT_FUNCTION(http_message_parser)
+{
+ zend_class_entry ce;
+
+ INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods);
+ php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+ memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new;
+ php_http_message_parser_object_handlers.clone_obj = NULL;
+
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC);
+
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC);
+ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC);
+
+ return SUCCESS;
+}
+
/*
* Local variables:
* tab-width: 4
PHP_HTTP_API void php_http_message_parser_dtor(php_http_message_parser_t *parser);
PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parser);
PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message);
-PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, unsigned flags, php_http_message_t **message);
+PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, php_http_message_t **message);
+
+typedef struct php_http_message_parser_object {
+ zend_object zo;
+ zend_object_value zv;
+ php_http_buffer_t *buffer;
+ php_http_message_parser_t *parser;
+} php_http_message_parser_object_t;
+
+PHP_HTTP_API zend_class_entry *php_http_message_parser_class_entry;
+
+PHP_MINIT_FUNCTION(http_message_parser);
+
+zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC);
+zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC);
+void php_http_message_parser_object_free(void *object TSRMLS_DC);
#endif
size_t php_http_boundary(char *buf, size_t buf_len TSRMLS_DC)
{
- return snprintf(buf, buf_len, "%15.15F", PHP_HTTP_G->env.request.time * php_combined_lcg(TSRMLS_C));
+ return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time(TSRMLS_C) * php_combined_lcg(TSRMLS_C));
}
int php_http_select_str(const char *cmp, int argc, ...)
#include "php_http_api.h"
+#ifdef PHP_HTTP_HAVE_IDN
+# include <idna.h>
+#endif
+
+#ifdef PHP_HTTP_HAVE_WCHAR
+# include <wchar.h>
+# include <wctype.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#include "php_http_utf8.h"
+
static inline char *localhostname(void)
{
char hostname[1024] = {0};
return SUCCESS;
}
+struct parse_state {
+ php_http_url_t url;
+#ifdef ZTS
+ void ***ts;
+#endif
+ const char *ptr;
+ const char *end;
+ size_t maxlen;
+ off_t offset;
+ unsigned flags;
+ char buffer[1]; /* last member */
+};
+
+void php_http_url_free(php_http_url_t **url)
+{
+ if (*url) {
+ efree(*url);
+ *url = NULL;
+ }
+}
+
+static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
+{
+ unsigned wchar;
+ size_t consumed = utf8towc(&wchar, (const unsigned char *) ptr, end - ptr);
+
+ if (!consumed || consumed == (size_t) -1) {
+ return 0;
+ }
+
+ if (wc) {
+ *wc = wchar;
+ }
+ return consumed;
+}
+
+#ifdef PHP_HTTP_HAVE_WCHAR
+static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
+{
+ wchar_t wchar;
+ size_t consumed = 0;
+#if defined(HAVE_MBRTOWC)
+ mbstate_t ps = {0};
+
+ consumed = mbrtowc(&wchar, ptr, end - ptr, &ps);
+#elif defined(HAVE_MBTOWC)
+ consumed = mbtowc(&wchar, ptr, end - ptr);
+#endif
+
+ if (!consumed || consumed == (size_t) -1) {
+ return 0;
+ }
+
+ if (wc) {
+ *wc = wchar;
+ }
+ return consumed;
+}
+#endif
+
+typedef enum parse_mb_what {
+ PARSE_SCHEME,
+ PARSE_USERINFO,
+ PARSE_HOSTINFO,
+ PARSE_PATH,
+ PARSE_QUERY,
+ PARSE_FRAGMENT
+} parse_mb_what_t;
+
+static const char * const parse_what[] = {
+ "scheme",
+ "userinfo",
+ "hostinfo",
+ "path",
+ "query",
+ "fragment"
+};
+
+static const char parse_xdigits[] = "0123456789ABCDEF";
+
+static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const char *ptr, const char *end, const char *begin, zend_bool silent)
+{
+ unsigned wchar;
+ size_t consumed = 0;
+
+ if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+ consumed = parse_mb_utf8(&wchar, ptr, end);
+ }
+#ifdef PHP_HTTP_HAVE_WCHAR
+ else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+ consumed = parse_mb_loc(&wchar, ptr, end);
+ }
+#endif
+
+ while (consumed) {
+ if (!(state->flags & PHP_HTTP_URL_PARSE_TOPCT) || what == PARSE_HOSTINFO || what == PARSE_SCHEME) {
+ if (what == PARSE_HOSTINFO && (state->flags & PHP_HTTP_URL_PARSE_TOIDN)) {
+ /* idna */
+ } else if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+ if (!isualnum(wchar)) {
+ break;
+ }
+#ifdef PHP_HTTP_HAVE_WCHAR
+ } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+ if (!iswalnum(wchar)) {
+ break;
+ }
+#endif
+ }
+ PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = *ptr++);
+ } else {
+ int i = 0;
+
+ PHP_HTTP_DUFF(consumed,
+ state->buffer[state->offset++] = '%';
+ state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) >> 4];
+ state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) & 0xf];
+ ++i;
+ );
+ }
+
+ return consumed;
+ }
+
+ if (!silent) {
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
+ parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin);
+ }
+
+ return 0;
+}
+
+static STATUS parse_userinfo(struct parse_state *state, const char *ptr)
+{
+ size_t mb;
+ const char *password = NULL, *end = state->ptr, *tmp = ptr;
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+
+ state->url.user = &state->buffer[state->offset];
+
+ do {
+ switch (*ptr) {
+ case ':':
+ if (password) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse password; duplicate ':' at pos %u in '%s'",
+ (unsigned) (ptr - tmp), tmp);
+ return FAILURE;
+ }
+ password = ptr + 1;
+ state->buffer[state->offset++] = 0;
+ state->url.pass = &state->buffer[state->offset];
+ break;
+
+ case '%':
+ if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'",
+ (unsigned) (ptr - tmp), tmp);
+ return FAILURE;
+ }
+ state->buffer[state->offset++] = *ptr++;
+ state->buffer[state->offset++] = *ptr++;
+ state->buffer[state->offset++] = *ptr;
+ break;
+
+ case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case ';': case '=': /* sub-delims */
+ case '-': case '.': case '_': case '~': /* unreserved */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ /* allowed */
+ state->buffer[state->offset++] = *ptr;
+ break;
+
+ default:
+ if (!(mb = parse_mb(state, PARSE_USERINFO, ptr, end, tmp, 0))) {
+ return FAILURE;
+ }
+ ptr += mb - 1;
+ }
+ } while(++ptr != end);
+
+
+ state->buffer[state->offset++] = 0;
+
+ return SUCCESS;
+}
+
+static STATUS parse_hostinfo(struct parse_state *state, const char *ptr)
+{
+ size_t mb, len;
+ const char *end = state->ptr, *tmp = ptr, *port = NULL;
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+
+
+#ifdef HAVE_INET_PTON
+ if (*ptr == '[') {
+ char *error = NULL, *tmp = memchr(ptr, ']', end - ptr);
+
+ if (tmp) {
+ size_t addrlen = tmp - ptr + 1;
+ char buf[16], *addr = estrndup(ptr + 1, addrlen - 2);
+ int rv = inet_pton(AF_INET6, addr, buf);
+
+ efree(addr);
+ if (rv == 1) {
+ state->buffer[state->offset] = '[';
+ state->url.host = &state->buffer[state->offset];
+ inet_ntop(AF_INET6, buf, state->url.host + 1, state->maxlen - state->offset);
+ state->offset += strlen(state->url.host);
+ state->buffer[state->offset++] = ']';
+ state->buffer[state->offset++] = 0;
+ ptr = tmp + 1;
+ } else if (rv == -1) {
+ error = strerror(errno);
+ } else {
+ error = "unexpected '['";
+ }
+ } else {
+ error = "expected ']'";
+ }
+
+ if (error) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse hostinfo; %s", error);
+ return FAILURE;
+ }
+ }
+#endif
+ if (ptr != end) do {
+ switch (*ptr) {
+ case ':':
+ if (port) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse port; unexpected ':' at pos %u in '%s'",
+ (unsigned) (ptr - tmp), tmp);
+ return FAILURE;
+ }
+ port = ptr + 1;
+ break;
+
+ case '%':
+ if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'",
+ (unsigned) (ptr - tmp), tmp);
+ return FAILURE;
+ }
+ state->buffer[state->offset++] = *ptr++;
+ state->buffer[state->offset++] = *ptr++;
+ state->buffer[state->offset++] = *ptr;
+ break;
+
+ case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case ';': case '=': /* sub-delims */
+ case '-': case '.': case '_': case '~': /* unreserved */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ if (port) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse port; unexpected char '%c' at pos %u in '%s'",
+ (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
+ return FAILURE;
+ }
+ /* no break */
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ /* allowed */
+ if (port) {
+ state->url.port *= 10;
+ state->url.port += *ptr - '0';
+ } else {
+ state->buffer[state->offset++] = *ptr;
+ }
+ break;
+
+ default:
+ if (port) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
+ (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
+ return FAILURE;
+ } else if (!(mb = parse_mb(state, PARSE_HOSTINFO, ptr, end, tmp, 0))) {
+ return FAILURE;
+ }
+ ptr += mb - 1;
+ }
+ } while (++ptr != end);
+
+ if (!state->url.host) {
+ len = (port ? port - tmp - 1 : end - tmp);
+ state->url.host = &state->buffer[state->offset - len];
+ state->buffer[state->offset++] = 0;
+ }
+
+#ifdef PHP_HTTP_HAVE_IDN
+ if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) {
+ char *idn = NULL;
+ int rv = -1;
+
+ if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+ rv = idna_to_ascii_8z(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
+ }
+# ifdef PHP_HTTP_HAVE_WCHAR
+ else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+ rv = idna_to_ascii_lz(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
+ }
+# endif
+ if (rv != IDNA_SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
+ return FAILURE;
+ } else {
+ size_t idnlen = strlen(idn);
+ memcpy(state->url.host, idn, idnlen + 1);
+ free(idn);
+ state->offset += idnlen - len;
+ }
+ }
+#endif
+
+ return SUCCESS;
+}
+
+static const char *parse_authority(struct parse_state *state)
+{
+ const char *tmp = state->ptr, *host = NULL;
+
+ do {
+ switch (*state->ptr) {
+ case '@':
+ /* userinfo delimiter */
+ if (host) {
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Failed to parse userinfo; unexpected '@'");
+ return NULL;
+ }
+ host = state->ptr + 1;
+ if (tmp != state->ptr && SUCCESS != parse_userinfo(state, tmp)) {
+ return NULL;
+ }
+ tmp = state->ptr + 1;
+ break;
+
+ case '/':
+ case '?':
+ case '#':
+ case '\0':
+ /* host delimiter */
+ if (tmp != state->ptr && SUCCESS != parse_hostinfo(state, tmp)) {
+ return NULL;
+ }
+ return state->ptr;
+ }
+ } while (++state->ptr <= state->end);
+
+ return NULL;
+}
+
+static const char *parse_path(struct parse_state *state)
+{
+ size_t mb;
+ const char *tmp;
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+
+ /* is there actually a path to parse? */
+ if (!*state->ptr) {
+ return state->ptr;
+ }
+ tmp = state->ptr;
+ state->url.path = &state->buffer[state->offset];
+
+ do {
+ 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;
+
+ 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,
+ "Failed to parse path; invalid percent encoding at pos %u in '%s'",
+ (unsigned) (state->ptr - tmp), tmp);
+ return NULL;
+ }
+ state->buffer[state->offset++] = *state->ptr++;
+ state->buffer[state->offset++] = *state->ptr++;
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ case '/': /* yeah, well */
+ case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case ';': case '=': /* sub-delims */
+ case '-': case '.': case '_': case '~': /* unreserved */
+ case ':': case '@': /* pchar */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ /* allowed */
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ default:
+ if (!(mb = parse_mb(state, PARSE_PATH, state->ptr, state->end, tmp, 0))) {
+ return NULL;
+ }
+ state->ptr += mb - 1;
+ }
+ } while (++state->ptr <= state->end);
+
+ return NULL;
+}
+
+static const char *parse_query(struct parse_state *state)
+{
+ size_t mb;
+ const char *tmp = state->ptr + !!*state->ptr;
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+
+ /* is there actually a query to parse? */
+ if (*state->ptr != '?') {
+ return state->ptr;
+ }
+
+ /* skip initial '?' */
+ tmp = ++state->ptr;
+ state->url.query = &state->buffer[state->offset];
+
+ do {
+ switch (*state->ptr) {
+ case '#':
+ 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,
+ "Failed to parse query; invalid percent encoding at pos %u in '%s'",
+ (unsigned) (state->ptr - tmp), tmp);
+ return NULL;
+ }
+ state->buffer[state->offset++] = *state->ptr++;
+ state->buffer[state->offset++] = *state->ptr++;
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ case '?': case '/': /* yeah, well */
+ case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case ';': case '=': /* sub-delims */
+ case '-': case '.': case '_': case '~': /* unreserved */
+ case ':': case '@': /* pchar */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ /* allowed */
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ default:
+ if (!(mb = parse_mb(state, PARSE_QUERY, state->ptr, state->end, tmp, 0))) {
+ return NULL;
+ }
+ state->ptr += mb - 1;
+ }
+ } while (++state->ptr <= state->end);
+
+ return NULL;
+}
+
+static const char *parse_fragment(struct parse_state *state)
+{
+ size_t mb;
+ const char *tmp;
+ TSRMLS_FETCH_FROM_CTX(state->ts);
+
+ /* is there actually a fragment to parse? */
+ if (*state->ptr != '#') {
+ return state->ptr;
+ }
+
+ /* skip initial '#' */
+ tmp = ++state->ptr;
+ state->url.fragment = &state->buffer[state->offset];
+
+ 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,
+ "Failed to parse fragment; invalid percent encoding at pos %u in '%s'",
+ (unsigned) (state->ptr - tmp), tmp);
+ return NULL;
+ }
+ state->buffer[state->offset++] = *state->ptr++;
+ state->buffer[state->offset++] = *state->ptr++;
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ case '?': case '/':
+ case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case ';': case '=': /* sub-delims */
+ case '-': case '.': case '_': case '~': /* unreserved */
+ case ':': case '@': /* pchar */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ /* allowed */
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ default:
+ if (!(mb = parse_mb(state, PARSE_FRAGMENT, state->ptr, state->end, tmp, 0))) {
+ return NULL;
+ }
+ state->ptr += mb - 1;
+ }
+ } while (++state->ptr <= state->end);
+
+ return NULL;
+}
+
+static const char *parse_hier(struct parse_state *state)
+{
+ if (*state->ptr == '/') {
+ if (state->end - state->ptr > 1) {
+ if (*(state->ptr + 1) == '/') {
+ state->ptr += 2;
+ if (!(state->ptr = parse_authority(state))) {
+ return NULL;
+ }
+ }
+ }
+ }
+ return parse_path(state);
+}
+
+static const char *parse_scheme(struct parse_state *state)
+{
+ size_t mb;
+ const char *tmp = state->ptr;
+
+ do {
+ switch (*state->ptr) {
+ case ':':
+ /* scheme delimiter */
+ state->url.scheme = &state->buffer[0];
+ state->buffer[state->offset++] = 0;
+ return ++state->ptr;
+
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ case '+': case '-': case '.':
+ if (state->ptr == tmp) {
+ return tmp;
+ }
+ /* no break */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ /* scheme part */
+ state->buffer[state->offset++] = *state->ptr;
+ break;
+
+ default:
+ if (!(mb = parse_mb(state, PARSE_SCHEME, state->ptr, state->end, tmp, 1))) {
+ /* soft fail; parse path next */
+ return tmp;
+ }
+ state->ptr += mb - 1;
+ }
+ } while (++state->ptr != state->end);
+
+ return tmp;
+}
+
+php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC)
+{
+ size_t maxlen = 3 * len;
+ struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen);
+
+ state->end = str + len;
+ state->ptr = str;
+ state->flags = flags;
+ state->maxlen = maxlen;
+ TSRMLS_SET_CTX(state->ts);
+
+ if (!parse_scheme(state)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr);
+ efree(state);
+ return NULL;
+ }
+
+ if (!parse_hier(state)) {
+ efree(state);
+ return NULL;
+ }
+
+ if (!parse_query(state)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL query: '%s'", state->ptr);
+ efree(state);
+ return NULL;
+ }
+
+ if (!parse_fragment(state)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr);
+ efree(state);
+ return NULL;
+ }
+
+ return (php_http_url_t *) state;
+}
+
ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct, 0, 0, 0)
ZEND_ARG_INFO(0, old_url)
ZEND_ARG_INFO(0, new_url)
php_url_free(purl);
}
+ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_parse, 0, 0, 1)
+ ZEND_ARG_INFO(0, url)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+PHP_METHOD(HttpUrl, parse)
+{
+ char *str;
+ int len;
+ long flags = 0;
+ php_http_url_t *url;
+ zend_error_handling zeh;
+
+ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags), invalid_arg, return);
+
+ zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
+ if ((url = php_http_url_parse(str, len, flags TSRMLS_CC))) {
+ object_init_ex(return_value, php_http_url_class_entry);
+ if (url->scheme) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("scheme"), url->scheme TSRMLS_CC);
+ }
+ if (url->user) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("user"), url->user TSRMLS_CC);
+ }
+ if (url->pass) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("pass"), url->pass TSRMLS_CC);
+ }
+ if (url->host) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("host"), url->host TSRMLS_CC);
+ }
+ if (url->port) {
+ zend_update_property_long(php_http_url_class_entry, return_value,
+ ZEND_STRL("port"), url->port TSRMLS_CC);
+ }
+ if (url->path) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("path"), url->path TSRMLS_CC);
+ }
+ if (url->query) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("query"), url->query TSRMLS_CC);
+ }
+ if (url->fragment) {
+ zend_update_property_string(php_http_url_class_entry, return_value,
+ ZEND_STRL("fragment"), url->fragment TSRMLS_CC);
+ }
+ php_http_url_free(&url);
+ }
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
static zend_function_entry php_http_url_methods[] = {
PHP_ME(HttpUrl, __construct, ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(HttpUrl, mod, ai_HttpUrl_mod, ZEND_ACC_PUBLIC)
PHP_ME(HttpUrl, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
ZEND_MALIAS(HttpUrl, __toString, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
PHP_ME(HttpUrl, toArray, ai_HttpUrl_toArray, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpUrl, parse, ai_HttpUrl_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
EMPTY_FUNCTION_ENTRY
};
zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC);
zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC);
+#ifdef PHP_HTTP_HAVE_WCHAR
+ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC TSRMLS_CC);
+#endif
+ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC);
+#ifdef PHP_HTTP_HAVE_IDN
+ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN TSRMLS_CC);
+#endif
+ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC);
+
return SUCCESS;
}
#define PHP_HTTP_URL_FROM_ENV 0x1000
#define PHP_HTTP_URL_SANITIZE_PATH 0x2000
+/* parse multibyte according to locale */
+#define PHP_HTTP_URL_PARSE_MBLOC 0x10000
+/* parse utf8 multibyte sequences */
+#define PHP_HTTP_URL_PARSE_MBUTF8 0x20000
+/* convert multibyte hostnames to IDNA */
+#define PHP_HTTP_URL_PARSE_TOIDN 0x100000
+/* percent encode multibyte sequences in userinfo, path, query and fragment */
+#define PHP_HTTP_URL_PARSE_TOPCT 0x200000
+
+typedef struct php_http_url {
+ /* compatible to php_url, but do not use php_url_free() */
+ char *scheme;
+ char *user;
+ char *pass;
+ char *host;
+ unsigned short port;
+ char *path;
+ char *query;
+ char *fragment;
+} 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);
+
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);
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);
--- /dev/null
+/*
+ +--------------------------------------------------------------------+
+ | 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_UTF8_H
+#define PHP_HTTP_UTF8_H
+
+typedef struct utf8_range {
+ unsigned int start;
+ unsigned int end;
+ unsigned char step;
+} utf8_range_t;
+
+static const unsigned char utf8_mblen[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
+};
+
+static const unsigned char utf8_mask[] = {
+ 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01
+};
+
+static const utf8_range_t utf8_ranges[] = {
+/* BEGIN::UTF8TABLE */
+ { 0x0041, 0x005A, 1},
+ { 0x0061, 0x007A, 1},
+ { 0x00AA, 0, 0},
+ { 0x00B5, 0, 0},
+ { 0x00BA, 0, 0},
+ { 0x00C0, 0x00D6, 1},
+ { 0x00D8, 0x00F6, 1},
+ { 0x00F8, 0x00FF, 1},
+ { 0x0100, 0x017F, 1},
+ { 0x0180, 0x024F, 1},
+ { 0x0250, 0x02AF, 1},
+ { 0x02B0, 0x02C1, 1},
+ { 0x02C6, 0x02D1, 1},
+ { 0x02E0, 0x02E4, 1},
+ { 0x02EE, 0, 0},
+ { 0x0345, 0, 0},
+ { 0x0370, 0x0373, 1},
+ { 0x0376, 0x0377, 1},
+ { 0x037A, 0x037D, 1},
+ { 0x0386, 0, 0},
+ { 0x0388, 0x038A, 1},
+ { 0x038C, 0, 0},
+ { 0x038E, 0x03A1, 1},
+ { 0x03A3, 0x03CE, 1},
+ { 0x03D0, 0x03F5, 1},
+ { 0x03F7, 0x03FF, 1},
+ { 0x0400, 0x0481, 1},
+ { 0x048A, 0x04FF, 1},
+ { 0x0500, 0x0523, 1},
+ { 0x0531, 0x0556, 1},
+ { 0x0559, 0, 0},
+ { 0x0561, 0x0587, 1},
+ { 0x05D0, 0x05EA, 1},
+ { 0x05F0, 0x05F2, 1},
+ { 0x0621, 0x064A, 1},
+ { 0x066E, 0x066F, 1},
+ { 0x0671, 0x06D3, 1},
+ { 0x06D5, 0, 0},
+ { 0x06E5, 0x06E6, 1},
+ { 0x06EE, 0x06EF, 1},
+ { 0x06FA, 0x06FC, 1},
+ { 0x06FF, 0, 0},
+ { 0x0710, 0, 0},
+ { 0x0712, 0x072F, 1},
+ { 0x074D, 0x074F, 1},
+ { 0x0750, 0x077F, 1},
+ { 0x0780, 0x07A5, 1},
+ { 0x07B1, 0, 0},
+ { 0x07C0, 0x07EA, 1},
+ { 0x07F4, 0x07F5, 1},
+ { 0x07FA, 0, 0},
+ { 0x0901, 0x0939, 1},
+ { 0x093C, 0x094D, 1},
+ { 0x0950, 0x0954, 1},
+ { 0x0958, 0x0961, 1},
+ { 0x0962, 0, 0},
+ { 0x0963, 0, 0},
+ { 0x0972, 0, 0},
+ { 0x097B, 0x097F, 1},
+ { 0x0981, 0x0983, 1},
+ { 0x0985, 0x098C, 1},
+ { 0x098F, 0, 0},
+ { 0x0990, 0, 0},
+ { 0x0993, 0x09A8, 1},
+ { 0x09AA, 0x09B0, 1},
+ { 0x09B2, 0, 0},
+ { 0x09B6, 0x09B9, 1},
+ { 0x09BC, 0x09C4, 1},
+ { 0x09C7, 0, 0},
+ { 0x09C8, 0, 0},
+ { 0x09CB, 0x09CE, 1},
+ { 0x09D7, 0, 0},
+ { 0x09DC, 0, 0},
+ { 0x09DD, 0, 0},
+ { 0x09DF, 0x09E3, 1},
+ { 0x09F0, 0x09FA, 1},
+ { 0x0A01, 0x0A03, 1},
+ { 0x0A05, 0x0A0A, 1},
+ { 0x0A0F, 0, 0},
+ { 0x0A10, 0, 0},
+ { 0x0A13, 0x0A28, 1},
+ { 0x0A2A, 0x0A30, 1},
+ { 0x0A32, 0, 0},
+ { 0x0A33, 0, 0},
+ { 0x0A35, 0, 0},
+ { 0x0A36, 0, 0},
+ { 0x0A38, 0, 0},
+ { 0x0A39, 0, 0},
+ { 0x0A3C, 0, 0},
+ { 0x0A3E, 0x0A42, 1},
+ { 0x0A47, 0, 0},
+ { 0x0A48, 0, 0},
+ { 0x0A4B, 0x0A4D, 1},
+ { 0x0A51, 0, 0},
+ { 0x0A59, 0x0A5C, 1},
+ { 0x0A5E, 0, 0},
+ { 0x0A70, 0x0A75, 1},
+ { 0x0A81, 0x0A83, 1},
+ { 0x0A85, 0x0A8D, 1},
+ { 0x0A8F, 0x0A91, 1},
+ { 0x0A93, 0x0AA8, 1},
+ { 0x0AAA, 0x0AB0, 1},
+ { 0x0AB2, 0, 0},
+ { 0x0AB3, 0, 0},
+ { 0x0AB5, 0x0AB9, 1},
+ { 0x0ABC, 0x0AC5, 1},
+ { 0x0AC7, 0x0AC9, 1},
+ { 0x0ACB, 0x0ACD, 1},
+ { 0x0AD0, 0, 0},
+ { 0x0AE0, 0x0AE3, 1},
+ { 0x0AF1, 0, 0},
+ { 0x0B01, 0x0B03, 1},
+ { 0x0B05, 0x0B0C, 1},
+ { 0x0B0F, 0, 0},
+ { 0x0B10, 0, 0},
+ { 0x0B13, 0x0B28, 1},
+ { 0x0B2A, 0x0B30, 1},
+ { 0x0B32, 0, 0},
+ { 0x0B33, 0, 0},
+ { 0x0B35, 0x0B39, 1},
+ { 0x0B3C, 0x0B44, 1},
+ { 0x0B47, 0x0B48, 1},
+ { 0x0B4B, 0x0B4D, 1},
+ { 0x0B56, 0x0B57, 1},
+ { 0x0B5C, 0, 0},
+ { 0x0B5D, 0, 0},
+ { 0x0B5F, 0x0B63, 1},
+ { 0x0B70, 0, 0},
+ { 0x0B71, 0, 0},
+ { 0x0B82, 0, 0},
+ { 0x0B83, 0, 0},
+ { 0x0B85, 0x0B8A, 1},
+ { 0x0B8E, 0x0B90, 1},
+ { 0x0B92, 0x0B95, 1},
+ { 0x0B99, 0, 0},
+ { 0x0B9A, 0, 0},
+ { 0x0B9C, 0, 0},
+ { 0x0B9E, 0, 0},
+ { 0x0B9F, 0, 0},
+ { 0x0BA3, 0, 0},
+ { 0x0BA4, 0, 0},
+ { 0x0BA8, 0x0BAA, 1},
+ { 0x0BAE, 0x0BB9, 1},
+ { 0x0BBE, 0x0BC2, 1},
+ { 0x0BC6, 0x0BC8, 1},
+ { 0x0BCA, 0x0BCD, 1},
+ { 0x0BD0, 0, 0},
+ { 0x0BD7, 0, 0},
+ { 0x0BF0, 0x0BFA, 1},
+ { 0x0C01, 0x0C03, 1},
+ { 0x0C05, 0x0C0C, 1},
+ { 0x0C0E, 0x0C10, 1},
+ { 0x0C12, 0x0C28, 1},
+ { 0x0C2A, 0x0C33, 1},
+ { 0x0C35, 0x0C39, 1},
+ { 0x0C3D, 0x0C44, 1},
+ { 0x0C46, 0x0C48, 1},
+ { 0x0C4A, 0x0C4D, 1},
+ { 0x0C55, 0x0C56, 1},
+ { 0x0C58, 0x0C59, 1},
+ { 0x0C60, 0x0C63, 1},
+ { 0x0C82, 0x0C83, 1},
+ { 0x0C85, 0x0C8C, 1},
+ { 0x0C8E, 0x0C90, 1},
+ { 0x0C92, 0x0CA8, 1},
+ { 0x0CAA, 0x0CB3, 1},
+ { 0x0CB5, 0x0CB9, 1},
+ { 0x0CBC, 0x0CC4, 1},
+ { 0x0CC6, 0x0CC8, 1},
+ { 0x0CCA, 0x0CCD, 1},
+ { 0x0CD5, 0x0CD6, 1},
+ { 0x0CDE, 0, 0},
+ { 0x0CE0, 0x0CE3, 1},
+ { 0x0CF1, 0, 0},
+ { 0x0CF2, 0, 0},
+ { 0x0D02, 0x0D03, 1},
+ { 0x0D05, 0x0D0C, 1},
+ { 0x0D0E, 0x0D10, 1},
+ { 0x0D12, 0x0D28, 1},
+ { 0x0D2A, 0x0D39, 1},
+ { 0x0D3D, 0x0D44, 1},
+ { 0x0D46, 0x0D48, 1},
+ { 0x0D4A, 0x0D4D, 1},
+ { 0x0D57, 0, 0},
+ { 0x0D60, 0x0D63, 1},
+ { 0x0D79, 0x0D7F, 1},
+ { 0x0D82, 0x0D83, 1},
+ { 0x0D85, 0x0D96, 1},
+ { 0x0D9A, 0x0DB1, 1},
+ { 0x0DB3, 0x0DBB, 1},
+ { 0x0DBD, 0, 0},
+ { 0x0DC0, 0x0DC6, 1},
+ { 0x0DCA, 0, 0},
+ { 0x0DCF, 0x0DD4, 1},
+ { 0x0DD6, 0, 0},
+ { 0x0DD8, 0x0DDF, 1},
+ { 0x0DF2, 0x0DF4, 1},
+ { 0x0E01, 0x0E2E, 1},
+ { 0x0E30, 0x0E3A, 1},
+ { 0x0E40, 0x0E45, 1},
+ { 0x0E47, 0x0E4E, 1},
+ { 0x0E81, 0x0E82, 1},
+ { 0x0E84, 0, 0},
+ { 0x0E87, 0x0E88, 1},
+ { 0x0E8A, 0, 0},
+ { 0x0E8D, 0, 0},
+ { 0x0E94, 0x0E97, 1},
+ { 0x0E99, 0x0E9F, 1},
+ { 0x0EA1, 0x0EA3, 1},
+ { 0x0EA5, 0, 0},
+ { 0x0EA7, 0, 0},
+ { 0x0EAA, 0x0EAB, 1},
+ { 0x0EAD, 0x0EB0, 1},
+ { 0x0EB2, 0x0EB3, 1},
+ { 0x0EBD, 0, 0},
+ { 0x0EC0, 0x0EC4, 1},
+ { 0x0EC6, 0, 0},
+ { 0x0EDC, 0x0EDD, 1},
+ { 0x0F00, 0, 0},
+ { 0x0F40, 0x0F47, 1},
+ { 0x0F49, 0x0F6C, 1},
+ { 0x0F88, 0x0F8B, 1},
+ { 0x1000, 0x102A, 1},
+ { 0x1050, 0x1055, 1},
+ { 0x105A, 0x105D, 1},
+ { 0x1061, 0, 0},
+ { 0x0165, 0, 0},
+ { 0x1066, 0, 0},
+ { 0x106E, 0x1070, 1},
+ { 0x1075, 0x1081, 1},
+ { 0x108E, 0, 0},
+ { 0x10A0, 0x10C5, 1},
+ { 0x10D0, 0x10FA, 1},
+ { 0x10FC, 0, 0},
+ { 0x1100, 0x1159, 1},
+ { 0x115F, 0x11A2, 1},
+ { 0x11A8, 0x11F9, 1},
+ { 0x1200, 0x1248, 1},
+ { 0x124A, 0x124D, 1},
+ { 0x1250, 0x1256, 1},
+ { 0x1258, 0, 0},
+ { 0x125A, 0x125D, 1},
+ { 0x1260, 0x1288, 1},
+ { 0x128A, 0x128D, 1},
+ { 0x1290, 0x12B0, 1},
+ { 0x12B2, 0x12B5, 1},
+ { 0x12B8, 0x12BE, 1},
+ { 0x12C0, 0, 0},
+ { 0x12C2, 0x12C5, 1},
+ { 0x12C8, 0x12D6, 1},
+ { 0x12D8, 0x1310, 1},
+ { 0x1312, 0x1315, 1},
+ { 0x1318, 0x135A, 1},
+ { 0x1380, 0x138F, 1},
+ { 0x13A0, 0x13F4, 1},
+ { 0x1401, 0x166C, 1},
+ { 0x166F, 0x1676, 1},
+ { 0x1681, 0x169A, 1},
+ { 0x16A0, 0x16EA, 1},
+ { 0x16EE, 0x16F0, 1},
+ { 0x1700, 0x170C, 1},
+ { 0x170E, 0x1711, 1},
+ { 0x1720, 0x1731, 1},
+ { 0x1740, 0x1751, 1},
+ { 0x1760, 0x176C, 1},
+ { 0x176E, 0x1770, 1},
+ { 0x1780, 0x17B3, 1},
+ { 0x17D7, 0, 0},
+ { 0x17DC, 0, 0},
+ { 0x1820, 0x1877, 1},
+ { 0x1880, 0x18A8, 1},
+ { 0x18AA, 0, 0},
+ { 0x1900, 0x191C, 1},
+ { 0x1946, 0x194F, 1},
+ { 0x1950, 0x196D, 1},
+ { 0x1970, 0x1974, 1},
+ { 0x1980, 0x19A9, 1},
+ { 0x19C1, 0x19C7, 1},
+ { 0x19D0, 0x19D9, 1},
+ { 0x1A00, 0x1A16, 1},
+ { 0x1B05, 0x1B33, 1},
+ { 0x1B45, 0x1B4B, 1},
+ { 0x1B50, 0x1B59, 1},
+ { 0x1B83, 0x1BA0, 1},
+ { 0x1BAE, 0x1BAF, 1},
+ { 0x1C00, 0x1C23, 1},
+ { 0x1C4D, 0x1C4F, 1},
+ { 0x1C5A, 0x1C7D, 1},
+ { 0x1D00, 0x1DBF, 1},
+ { 0x1E00, 0x1E9F, 1},
+ { 0x1EA0, 0x1EFF, 1},
+ { 0x1F00, 0x1F15, 1},
+ { 0x1F18, 0x1F1D, 1},
+ { 0x1F20, 0x1F45, 1},
+ { 0x1F48, 0x1F4D, 1},
+ { 0x1F50, 0x1F57, 1},
+ { 0x1F59, 0, 0},
+ { 0x1F5B, 0, 0},
+ { 0x1F5D, 0, 0},
+ { 0x1F5F, 0x1F7D, 1},
+ { 0x1F80, 0x1FB4, 1},
+ { 0x1FB6, 0x1FBC, 1},
+ { 0x1FBE, 0, 0},
+ { 0x1FC2, 0x1FC4, 1},
+ { 0x1FC6, 0x1FCC, 1},
+ { 0x1FD0, 0x1FD3, 1},
+ { 0x1FD6, 0x1FDB, 1},
+ { 0x1FE0, 0x1FEC, 1},
+ { 0x1FF2, 0x1FF4, 1},
+ { 0x1FF6, 0x1FFC, 1},
+ { 0x2071, 0, 0},
+ { 0x207F, 0, 0},
+ { 0x2090, 0x2094, 1},
+ { 0x2102, 0, 0},
+ { 0x2107, 0, 0},
+ { 0x210A, 0x2113, 1},
+ { 0x2115, 0, 0},
+ { 0x2119, 0x211D, 1},
+ { 0x2124, 0, 0},
+ { 0x2126, 0, 0},
+ { 0x2128, 0x212D, 1},
+ { 0x212F, 0x2139, 1},
+ { 0x213C, 0x213F, 1},
+ { 0x2145, 0x2149, 1},
+ { 0x214E, 0, 0},
+ { 0x2160, 0x2188, 1},
+ { 0x249C, 0x24E9, 1},
+ { 0x2C00, 0x2C2E, 1},
+ { 0x2C30, 0x2C5E, 1},
+ { 0x2C60, 0x2C6F, 1},
+ { 0x2C71, 0x2C7D, 1},
+ { 0x2C80, 0x2CE4, 1},
+ { 0x2D00, 0x2D25, 1},
+ { 0x2D30, 0x2D65, 1},
+ { 0x2D6F, 0, 0},
+ { 0x2D80, 0x2D96, 1},
+ { 0x2DA0, 0x2DA6, 1},
+ { 0x2DA8, 0x2DAE, 1},
+ { 0x2DB0, 0x2DB6, 1},
+ { 0x2DB8, 0x2DBE, 1},
+ { 0x2DC0, 0x2DC6, 1},
+ { 0x2DC8, 0x2DCE, 1},
+ { 0x2DD0, 0x2DD6, 1},
+ { 0x2DD8, 0x2DDE, 1},
+ { 0x3005, 0x3007, 1},
+ { 0x3021, 0x3029, 1},
+ { 0x3031, 0x3035, 1},
+ { 0x3038, 0x303C, 1},
+ { 0x3041, 0x3096, 1},
+ { 0x309D, 0x309F, 1},
+ { 0x30A1, 0x30FA, 1},
+ { 0x30FC, 0x30FF, 1},
+ { 0x3105, 0x312D, 1},
+ { 0x3131, 0x318E, 1},
+ { 0x31A0, 0x31B7, 1},
+ { 0x31F0, 0x31FF, 1},
+ { 0x3400, 0x4DB5, 1},
+ { 0x4E00, 0x9FBB, 1},
+ { 0xA000, 0xA48C, 1},
+ { 0xA500, 0xA60B, 1},
+ { 0xA610, 0xA61F, 1},
+ { 0xA62A, 0xA62B, 1},
+ { 0xA640, 0xA65F, 1},
+ { 0xA662, 0xA66E, 1},
+ { 0xA680, 0xA697, 1},
+ { 0xA717, 0xA71F, 1},
+ { 0xA722, 0xA78C, 1},
+ { 0xA7FB, 0xA7FF, 1},
+ { 0xA800, 0, 0},
+ { 0xA801, 0, 0},
+ { 0xA803, 0xA805, 1},
+ { 0xA807, 0xA80A, 1},
+ { 0xA80C, 0xA822, 1},
+ { 0xA840, 0xA873, 1},
+ { 0xA882, 0xA8B3, 1},
+ { 0xA90A, 0xA92D, 1},
+ { 0xA930, 0xA946, 1},
+ { 0xAA00, 0xAA28, 1},
+ { 0xAA40, 0xAA42, 1},
+ { 0xAA44, 0xAA4B, 1},
+ { 0xAC00, 0xD7A3, 1},
+ { 0xF900, 0xFA2D, 1},
+ { 0xFA30, 0xFA6A, 1},
+ { 0xFA70, 0xFAD9, 1},
+ { 0xFB00, 0xFB06, 1},
+ { 0xFB13, 0xFB17, 1},
+ { 0xFB1D, 0, 0},
+ { 0xFB1F, 0xFB28, 1},
+ { 0xFB2A, 0xFB36, 1},
+ { 0xFB38, 0xFB3C, 1},
+ { 0xFB3E, 0, 0},
+ { 0xFB40, 0, 0},
+ { 0xFB41, 0, 0},
+ { 0xFB43, 0, 0},
+ { 0xFB44, 0, 0},
+ { 0xFB46, 0xFB4F, 1},
+ { 0xFB50, 0xFBB1, 1},
+ { 0xFBD3, 0xFD3D, 1},
+ { 0xFD50, 0xFD8F, 1},
+ { 0xFD92, 0xFDC7, 1},
+ { 0xFDF0, 0xFDFB, 1},
+ { 0xFE70, 0xFE74, 1},
+ { 0xFE76, 0xFEFC, 1},
+ { 0xFF21, 0xFF3A, 1},
+ { 0xFF41, 0xFF5A, 1},
+ { 0xFF66, 0xFFBE, 1},
+ { 0xFFC2, 0xFFC7, 1},
+ { 0xFFCA, 0xFFCF, 1},
+ { 0xFFD2, 0xFFD7, 1},
+ { 0xFFDA, 0xFFDC, 1},
+ {0x00010000, 0x0001000B, 1},
+ {0x0001000D, 0x00010026, 1},
+ {0x00010028, 0x0001003A, 1},
+ {0x0001003C, 0x0001003D, 1},
+ {0x0001003F, 0x0001004D, 1},
+ {0x00010050, 0x0001005D, 1},
+ {0x00010080, 0x000100FA, 1},
+ {0x00010140, 0x00010174, 1},
+ {0x00010280, 0x0001029C, 1},
+ {0x000102A0, 0x000102D0, 1},
+ {0x00010300, 0x0001031E, 1},
+ {0x00010330, 0x0001034A, 1},
+ {0x00010380, 0x0001039D, 1},
+ {0x000103A0, 0x000103C3, 1},
+ {0x000103C8, 0x000103CF, 1},
+ {0x000103D1, 0x000103D5, 1},
+ {0x00010400, 0x0001044F, 1},
+ {0x00010450, 0x0001047F, 1},
+ {0x00010480, 0x0001049D, 1},
+ {0x000104A0, 0x000104A9, 1},
+ {0x00010800, 0x00010805, 1},
+ {0x00010808, 0, 0},
+ {0x0001080A, 0x00010835, 1},
+ {0x00010837, 0x00010838, 1},
+ {0x0001083C, 0, 0},
+ {0x0001083F, 0, 0},
+ {0x00010900, 0x00010915, 1},
+ {0x00010A00, 0, 0},
+ {0x00010A10, 0x00010A13, 1},
+ {0x00010A15, 0x00010A17, 1},
+ {0x00010A19, 0x00010A33, 1},
+ {0x00012000, 0x0001236E, 1},
+ {0x00012400, 0x00012462, 1},
+ {0x0001D400, 0x0001D454, 1},
+ {0x0001D456, 0x0001D49C, 1},
+ {0x0001D49E, 0x0001D49F, 1},
+ {0x0001D4A2, 0, 0},
+ {0x0001D4A5, 0x0001D4A6, 1},
+ {0x0001D4A9, 0x0001D4AC, 1},
+ {0x0001D4AE, 0x0001D4B9, 1},
+ {0x0001D4BB, 0, 0},
+ {0x0001D4BD, 0x0001D4C3, 1},
+ {0x0001D4C5, 0x0001D505, 1},
+ {0x0001D507, 0x0001D50A, 1},
+ {0x0001D50D, 0x0001D514, 1},
+ {0x0001D516, 0x0001D51C, 1},
+ {0x0001D51E, 0x0001D539, 1},
+ {0x0001D53B, 0x0001D53E, 1},
+ {0x0001D540, 0x0001D544, 1},
+ {0x0001D546, 0, 0},
+ {0x0001D54A, 0x0001D550, 1},
+ {0x0001D552, 0x0001D6A5, 1},
+ {0x0001D6A8, 0x0001D6C0, 1},
+ {0x0001D6C2, 0x0001D6DA, 1},
+ {0x0001D6DC, 0x0001D6FA, 1},
+ {0x0001D6FC, 0x0001D714, 1},
+ {0x0001D716, 0x0001D734, 1},
+ {0x0001D736, 0x0001D74E, 1},
+ {0x0001D750, 0x0001D76E, 1},
+ {0x0001D770, 0x0001D788, 1},
+ {0x0001D78A, 0x0001D7A8, 1},
+ {0x0001D7AA, 0x0001D7C2, 1},
+ {0x0001D7C4, 0x0001D7CB, 1},
+ {0x0001D7CE, 0x0001D7FF, 1},
+ {0x00020000, 0x0002A6D6, 1},
+ {0x0002F800, 0x0002FA1D, 1},
+ { 0x0660, 0x0669, 1},
+ { 0x06F0, 0x06F9, 1},
+ { 0x0966, 0x096F, 1},
+ { 0x09E6, 0x09EF, 1},
+ { 0x0A66, 0x0A6F, 1},
+ { 0x0AE6, 0x0AEF, 1},
+ { 0x0B66, 0x0B6F, 1},
+ { 0x0BE6, 0x0BEF, 1},
+ { 0x0C66, 0x0C6F, 1},
+ { 0x0C78, 0x0C7F, 1},
+ { 0x0CE6, 0x0CEF, 1},
+ { 0x0D66, 0x0D75, 1},
+ { 0x0D70, 0x0D75, 1},
+ { 0x0E50, 0x0E59, 1},
+ { 0x0ED0, 0x0ED9, 1},
+ { 0x0F20, 0x0F29, 1},
+ { 0x1040, 0x1049, 1},
+ { 0x17E0, 0x17E9, 1},
+ { 0x1810, 0x1819, 1},
+ { 0x1BB0, 0x1BB9, 1},
+ { 0x1C40, 0x1C49, 1},
+ { 0x1C50, 0x1C59, 1},
+ { 0xA620, 0xA629, 1},
+ { 0xA8D0, 0xA8D9, 1},
+ { 0xA900, 0xA909, 1},
+ { 0xAA50, 0xAA59, 1},
+ { 0xFF10, 0xFF19, 1},
+
+/* END::UTF8TABLE */
+};
+
+static inline size_t utf8towc(unsigned *wc, const unsigned char *uc, size_t len)
+{
+ unsigned char ub = utf8_mblen[*uc];
+
+ if (!ub || ub > len || ub > 3) {
+ return 0;
+ }
+
+ *wc = *uc & utf8_mask[ub];
+
+ switch (ub) {
+ case 4:
+ if ((uc[1] & 0xc0) != 0x80) {
+ return 0;
+ }
+ *wc <<= 6;
+ *wc += *++uc & 0x3f;
+ /* no break */
+ case 3:
+ if ((uc[1] & 0xc0) != 0x80) {
+ return 0;
+ }
+ *wc <<= 6;
+ *wc += *++uc & 0x3f;
+ /* no break */
+ case 2:
+ if ((uc[1] & 0xc0) != 0x80) {
+ return 0;
+ }
+ *wc <<= 6;
+ *wc += *++uc & 0x3f;
+ /* no break */
+ case 1:
+ break;
+
+ default:
+ return 0;
+ }
+
+ return ub;
+}
+
+static inline zend_bool isualpha(unsigned ch)
+{
+ unsigned i, j;
+
+ for (i = 0; i < sizeof(utf8_ranges)/sizeof(utf8_range_t); ++i) {
+ if (utf8_ranges[i].start == ch) {
+ return 1;
+ } else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) {
+ if (utf8_ranges[i].step == 1) {
+ return 1;
+ }
+ for (j = utf8_ranges[i].start; j <= utf8_ranges[i].end; j+= utf8_ranges[i].step) {
+ if (ch == j) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static inline zend_bool isualnum(unsigned ch)
+{
+ /* digits */
+ if (ch >= 0x30 && ch <= 0x39) {
+ return 1;
+ }
+ return isualpha(ch);
+}
+
+#endif /* PHP_HTTP_UTF8_H */
+
+/*
+ * 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
+ */
--- /dev/null
+--TEST--
+client once & wait with events
+--SKIPIF--
+<?php
+include "skipif.inc";
+try {
+ $client = new http\Client;
+ if (!$client->enableEvents())
+ throw new Exception("need events support");
+} catch (Exception $e) {
+ die("skip ".$e->getMessage());
+}
+skip_online_test();
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$request = new http\Client\Request("GET", "http://www.example.org/");
+
+foreach (http\Client::getAvailableDrivers() as $driver) {
+ $client = new http\Client($driver);
+ $client->enableEvents(true);
+ $client->enqueue($request);
+
+ while ($client->once()) {
+ $client->wait(.1);
+ }
+
+ if (!$client->getResponse()) {
+ var_dump($client);
+ }
+}
+?>
+Done
+--EXPECT--
+Test
+Done
User-Agent: curl/7.24.0 (x86_64-unknown-linux-gnu) libcurl/7.24.0 OpenSSL/1.0.0g zlib/1.2.6 libssh2/1.3.0
Host: drop
Accept: */*
-Content-Length: 2284
+Content-Length: 2273
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------6e182425881c
["type":protected]=>
int(1)
["body":protected]=>
- object(http\Message\Body)#%d (0) {
- }
+ NULL
["requestMethod":protected]=>
string(3) "GET"
["requestUrl":protected]=>
["type":protected]=>
int(1)
["body":protected]=>
- object(http\Message\Body)#%d (0) {
- }
+ NULL
["requestMethod":protected]=>
string(4) "POST"
["requestUrl":protected]=>
["type":protected]=>
int(0)
["body":protected]=>
- object(http\Message\Body)#%d (0) {
- }
+ NULL
["requestMethod":protected]=>
string(0) ""
["requestUrl":protected]=>
--- /dev/null
+--TEST--
+message parser
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+use http\Message\Parser;
+
+foreach (glob(__DIR__."/data/message_*.txt") as $file) {
+ $parser = new Parser;
+ $fd = fopen($file, "r") or die("Could not open $file");
+ while (!feof($fd)) {
+ switch ($parser->parse(fgets($fd), 0, $message)) {
+ case Parser::STATE_DONE:
+ $string = (string) $message;
+ break 2;
+ case Parser::STATE_FAILURE:
+ throw new Exception(($e = error_get_last()) ? $e["message"] : "Could not parse $file");
+ }
+ }
+
+ $parser = new Parser;
+ rewind($fd);
+ unset($message);
+
+ switch ($parser->stream($fd, 0, $message)) {
+ case Parser::STATE_DONE:
+ case Parser::STATE_START:
+ break;
+ default:
+ printf("Expected parser state 0 or 8, got %d", $parser->getState());
+ }
+ if ($string !== (string) $message) {
+ $a = explode("\n", $string);
+ $b = explode("\n", (string) $message);
+ while ((null !== ($aa = array_shift($a))) || (null !== ($bb = array_shift($b)))) {
+ if ($aa !== $bb) {
+ isset($aa) and printf("-- %s\n", $aa);
+ isset($bb) and printf("++ %s\n", $bb);
+ }
+ }
+ }
+}
+?>
+DONE
+--EXPECT--
+Test
+DONE
--- /dev/null
+--TEST--
+message parser with nonblocking stream
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$parser = new http\Message\Parser;
+$socket = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+stream_set_blocking($socket[0], 0);
+
+$message = array(
+"GET / HTTP/1.1\n",
+"Host: localhost\n",
+"Content-length: 3\n",
+"\n",
+"OK\n"
+);
+
+while ($message) {
+ $line = array_shift($message);
+ $parser->stream($socket[0], 0, $msg);
+ fwrite($socket[1], $line);
+ $parser->stream($socket[0], 0, $msg);
+}
+
+var_dump($msg, (string) $msg->getBody());
+
+?>
+DONE
+--EXPECTF--
+Test
+object(http\Message)#%d (9) {
+ ["type":protected]=>
+ int(1)
+ ["body":protected]=>
+ object(http\Message\Body)#2 (0) {
+ }
+ ["requestMethod":protected]=>
+ string(3) "GET"
+ ["requestUrl":protected]=>
+ string(1) "/"
+ ["responseStatus":protected]=>
+ string(0) ""
+ ["responseCode":protected]=>
+ int(0)
+ ["httpVersion":protected]=>
+ string(3) "1.1"
+ ["headers":protected]=>
+ array(3) {
+ ["Host"]=>
+ string(9) "localhost"
+ ["Content-Length"]=>
+ int(3)
+ ["X-Original-Content-Length"]=>
+ string(1) "3"
+ }
+ ["parentMessage":protected]=>
+ NULL
+}
+string(3) "OK
+"
+DONE
\ No newline at end of file
--- /dev/null
+--TEST--
+url parser
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s:",
+ "ss:",
+ "s:a",
+ "ss:aa",
+ "s://",
+ "ss://",
+ "s://a",
+ "ss://aa",
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url));
+}
+
+?>
+DONE
+--EXPECTF--
+Test
+
+s:
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss:
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s:a
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "a"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss:aa
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(2) "aa"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss://
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://a
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "a"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss://aa
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(2) "aa"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser with paths
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s:a/",
+ "ss:aa/",
+ "s:/a/",
+ "ss:/aa/",
+ "s://a/",
+ "s://h/a",
+ "ss://hh/aa",
+ "s:///a/b",
+ "ss:///aa/bb",
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url));
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+s:a/
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(2) "a/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss:aa/
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(3) "aa/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s:/a/
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(3) "/a/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss:/aa/
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(4) "/aa/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://a/
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "a"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://h/a
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(2) "/a"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss://hh/aa
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(2) "hh"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(3) "/aa"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s:///a/b
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(4) "/a/b"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+ss:///aa/bb
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(6) "/aa/bb"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser with query
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s:?q",
+ "ss:?qq",
+ "s:/?q",
+ "ss:/?qq",
+ "s://?q",
+ "ss://?qq",
+ "s://h?q",
+ "ss://hh?qq",
+ "s://h/p?q",
+ "ss://hh/pp?qq",
+ "s://h:123/p/?q",
+ "ss://hh:123/pp/?qq",
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url));
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+s:?q
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ string(1) "q"
+ ["fragment"]=>
+ NULL
+}
+
+ss:?qq
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ string(2) "qq"
+ ["fragment"]=>
+ NULL
+}
+
+s:/?q
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ string(1) "q"
+ ["fragment"]=>
+ NULL
+}
+
+ss:/?qq
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ string(2) "qq"
+ ["fragment"]=>
+ NULL
+}
+
+s://?q
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ string(1) "q"
+ ["fragment"]=>
+ NULL
+}
+
+ss://?qq
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ string(2) "qq"
+ ["fragment"]=>
+ NULL
+}
+
+s://h?q
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ string(1) "q"
+ ["fragment"]=>
+ NULL
+}
+
+ss://hh?qq
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(2) "hh"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ string(2) "qq"
+ ["fragment"]=>
+ NULL
+}
+
+s://h/p?q
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(2) "/p"
+ ["query"]=>
+ string(1) "q"
+ ["fragment"]=>
+ NULL
+}
+
+ss://hh/pp?qq
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(2) "hh"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(3) "/pp"
+ ["query"]=>
+ string(2) "qq"
+ ["fragment"]=>
+ NULL
+}
+
+s://h:123/p/?q
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ int(123)
+ ["path"]=>
+ string(3) "/p/"
+ ["query"]=>
+ string(1) "q"
+ ["fragment"]=>
+ NULL
+}
+
+ss://hh:123/pp/?qq
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(2) "ss"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(2) "hh"
+ ["port"]=>
+ int(123)
+ ["path"]=>
+ string(4) "/pp/"
+ ["query"]=>
+ string(2) "qq"
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser multibyte/locale
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!defined("http\\Url::PARSE_MBLOC") or
+ !stristr(setlocale(LC_CTYPE, NULL), "utf")) {
+ die("skip need http\\Url::PARSE_MBLOC support and LC_CTYPE=*.UTF-8");
+}
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s\xc3\xa7heme:",
+ "s\xc3\xa7heme://h\xc6\x9fst",
+ "s\xc3\xa7heme://h\xc6\x9fst:23/päth/öf/fıle"
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url, http\Url::PARSE_MBLOC));
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+sçheme:
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(5) "hƟst"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst:23/päth/öf/fıle
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(5) "hƟst"
+ ["port"]=>
+ int(23)
+ ["path"]=>
+ string(16) "/päth/öf/fıle"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser multibyte/utf-8
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s\xc3\xa7heme:",
+ "s\xc3\xa7heme://h\xc6\x9fst",
+ "s\xc3\xa7heme://h\xc6\x9fst:23/päth/öf/fıle"
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url, http\Url::PARSE_MBUTF8));
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+sçheme:
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(5) "hƟst"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst:23/päth/öf/fıle
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(5) "hƟst"
+ ["port"]=>
+ int(23)
+ ["path"]=>
+ string(16) "/päth/öf/fıle"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser multibyte/locale/idna
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!defined("http\\Url::PARSE_MBLOC") or
+ !defined("http\\Url::PARSE_TOIDN") or
+ !stristr(setlocale(LC_CTYPE, NULL), ".utf")) {
+ die("skip need http\\Url::PARSE_MBLOC|http\\Url::PARSE_TOIDN support and LC_CTYPE=*.UTF-8");
+}
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s\xc3\xa7heme:",
+ "s\xc3\xa7heme://h\xc6\x9fst",
+ "s\xc3\xa7heme://h\xc6\x9fst:23/päth/öf/fıle"
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url, http\Url::PARSE_MBLOC|http\Url::PARSE_TOIDN));
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+sçheme:
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(11) "xn--hst-kwb"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst:23/päth/öf/fıle
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(11) "xn--hst-kwb"
+ ["port"]=>
+ int(23)
+ ["path"]=>
+ string(16) "/päth/öf/fıle"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser multibyte/utf-8/idna
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!defined("http\\Url::PARSE_TOIDN")) {
+ die("skip need http\\Url::PARSE_TOIDN support");
+}
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s\xc3\xa7heme:",
+ "s\xc3\xa7heme://h\xc6\x9fst",
+ "s\xc3\xa7heme://h\xc6\x9fst:23/päth/öf/fıle"
+);
+
+foreach ($urls as $url) {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url, http\Url::PARSE_MBUTF8|http\Url::PARSE_TOIDN));
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+sçheme:
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(11) "xn--hst-kwb"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+sçheme://hƟst:23/päth/öf/fıle
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(7) "sçheme"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(11) "xn--hst-kwb"
+ ["port"]=>
+ int(23)
+ ["path"]=>
+ string(16) "/päth/öf/fıle"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser ipv6
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s://[a:80",
+ "s://[0]",
+ "s://[::1]:80",
+ "s://mike@[0:0:0:0:0:FFFF:204.152.189.116]/foo",
+);
+
+foreach ($urls as $url) {
+ try {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url));
+ } catch (Exception $e) {
+ echo $e->getMessage(),"\n";
+ }
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+s://[a:80
+http\Url::parse(): Failed to parse hostinfo; expected ']'
+
+s://[0]
+http\Url::parse(): Failed to parse hostinfo; unexpected '['
+
+s://[::1]:80
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ NULL
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(5) "[::1]"
+ ["port"]=>
+ int(80)
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://mike@[0:0:0:0:0:FFFF:204.152.189.116]/foo
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "mike"
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(24) "[::ffff:204.152.189.116]"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(4) "/foo"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser userinfo
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "s://:@",
+ "s://u@",
+ "s://u:@",
+ "s://u:p@",
+ "s://user:pass@",
+ "s://user:pass@host",
+ "s://u@h",
+ "s://user@h",
+ "s://u@host",
+ "s://user:p@h",
+ "s://user:pass@h",
+ "s://user:pass@host",
+);
+
+foreach ($urls as $url) {
+ try {
+ printf("\n%s\n", $url);
+ var_dump(http\Url::parse($url));
+ } catch (Exception $e) {
+ echo $e->getMessage(),"\n";
+ }
+}
+?>
+DONE
+--EXPECTF--
+Test
+
+s://:@
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(0) ""
+ ["pass"]=>
+ string(0) ""
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://u@
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(1) "u"
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://u:@
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(1) "u"
+ ["pass"]=>
+ string(0) ""
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://u:p@
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(1) "u"
+ ["pass"]=>
+ string(1) "p"
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://user:pass@
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "user"
+ ["pass"]=>
+ string(4) "pass"
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://user:pass@host
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "user"
+ ["pass"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(4) "host"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://u@h
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(1) "u"
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://user@h
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "user"
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://u@host
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(1) "u"
+ ["pass"]=>
+ NULL
+ ["host"]=>
+ string(4) "host"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://user:p@h
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "user"
+ ["pass"]=>
+ string(1) "p"
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://user:pass@h
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "user"
+ ["pass"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(1) "h"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+
+s://user:pass@host
+object(http\Url)#1 (8) {
+ ["scheme"]=>
+ string(1) "s"
+ ["user"]=>
+ string(4) "user"
+ ["pass"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(4) "host"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ NULL
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+DONE
--- /dev/null
+--TEST--
+url parser multibyte/utf-8/topct
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$urls = array(
+ "http://mike:paßwort@sörver.net/for/€/?by=¢#ø"
+);
+
+foreach ($urls as $url) {
+ var_dump(http\Url::parse($url, http\Url::PARSE_MBUTF8|http\Url::PARSE_TOPCT));
+}
+?>
+DONE
+--EXPECTF--
+Test
+object(http\Url)#%d (8) {
+ ["scheme"]=>
+ string(4) "http"
+ ["user"]=>
+ string(4) "mike"
+ ["pass"]=>
+ string(12) "pa%C3%9Fwort"
+ ["host"]=>
+ string(11) "sörver.net"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(15) "/for/%E2%82%AC/"
+ ["query"]=>
+ string(9) "by=%C2%A2"
+ ["fragment"]=>
+ string(6) "%C3%B8"
+}
+DONE