From c38309b2d445583e024de648cbe22fb1a9f1f37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Sat, 5 Jul 2025 20:06:24 +0200 Subject: [PATCH 1/5] uri: Streamline implementation of `uriparser_parse_uri_ex()` Avoid the use of a macro and streamline the logic. --- ext/uri/php_uriparser.c | 64 ++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/ext/uri/php_uriparser.c b/ext/uri/php_uriparser.c index ba6f1ce776cb..a18eb87bc15b 100644 --- a/ext/uri/php_uriparser.c +++ b/ext/uri/php_uriparser.c @@ -297,47 +297,51 @@ static void throw_invalid_uri_exception(void) { zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); } - -#define PARSE_URI(dest_uri, uri_str, uriparser_uris, silent) \ - do { \ - if (ZSTR_LEN(uri_str) == 0 || \ - uriParseSingleUriExMmA(dest_uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS \ - ) { \ - efree(uriparser_uris); \ - if (!silent) { \ - throw_invalid_uri_exception(); \ - } \ - return NULL; \ - } \ - } while (0) - void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t *uriparser_base_urls, bool silent) { - uriparser_uris_t *uriparser_uris = uriparser_create_uris(); - - if (uriparser_base_urls == NULL) { - PARSE_URI(&uriparser_uris->uri, uri_str, uriparser_uris, silent); - uriMakeOwnerMmA(&uriparser_uris->uri, mm); - } else { - UriUriA uri; + UriUriA uri = {0}; - PARSE_URI(&uri, uri_str, uriparser_uris, silent); + /* Empty URIs are always invalid. */ + if (ZSTR_LEN(uri_str) == 0) { + goto fail; + } + + /* Parse the URI. */ + if (uriParseSingleUriExMmA(&uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS) { + goto fail; + } - if (uriAddBaseUriExMmA(&uriparser_uris->uri, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm) != URI_SUCCESS) { - efree(uriparser_uris); - uriFreeUriMembersMmA(&uri, mm); - if (!silent) { - throw_invalid_uri_exception(); - } + if (uriparser_base_urls != NULL) { + UriUriA tmp = {0}; - return NULL; + /* Combine the parsed URI with the base URI and store the result in 'tmp', + * since the target and source URLs must be distinct. */ + if (uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm) != URI_SUCCESS) { + goto fail; } - uriMakeOwnerMmA(&uriparser_uris->uri, mm); + /* Store the combined URI back into 'uri'. */ uriFreeUriMembersMmA(&uri, mm); + uri = tmp; } + /* Make the resulting URI independent of the 'uri_str'. */ + uriMakeOwnerMmA(&uri, mm); + + uriparser_uris_t *uriparser_uris = uriparser_create_uris(); + uriparser_uris->uri = uri; + return uriparser_uris; + + fail: + + uriFreeUriMembersMmA(&uri, mm); + + if (!silent) { + throw_invalid_uri_exception(); + } + + return NULL; } void *uriparser_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent) From 7e2ba9d1cb64b6ae11ba64702d71d2263b68136c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Sat, 5 Jul 2025 20:15:41 +0200 Subject: [PATCH 2/5] uri: Improve exceptions for `Uri\Rfc3986\Uri` --- ext/uri/php_uriparser.c | 32 ++++++++++++++++++++++---------- ext/uri/tests/004.phpt | 2 +- ext/uri/tests/055.phpt | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/ext/uri/php_uriparser.c b/ext/uri/php_uriparser.c index a18eb87bc15b..51922b530b0a 100644 --- a/ext/uri/php_uriparser.c +++ b/ext/uri/php_uriparser.c @@ -21,7 +21,6 @@ #include "Zend/zend_exceptions.h" static void uriparser_free_uri(void *uri); -static void throw_invalid_uri_exception(void); static void *uriparser_malloc(UriMemoryManager *memory_manager, size_t size) { @@ -293,21 +292,25 @@ static uriparser_uris_t *uriparser_create_uris(void) return uriparser_uris; } -static void throw_invalid_uri_exception(void) -{ - zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); -} void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t *uriparser_base_urls, bool silent) { UriUriA uri = {0}; /* Empty URIs are always invalid. */ if (ZSTR_LEN(uri_str) == 0) { + if (!silent) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI must not be empty", 0); + } + goto fail; } /* Parse the URI. */ if (uriParseSingleUriExMmA(&uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS) { + if (!silent) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); + } + goto fail; } @@ -316,7 +319,20 @@ void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t /* Combine the parsed URI with the base URI and store the result in 'tmp', * since the target and source URLs must be distinct. */ - if (uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm) != URI_SUCCESS) { + int result = uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm); + if (result != URI_SUCCESS) { + if (!silent) { + switch (result) { + case URI_ERROR_ADDBASE_REL_BASE: + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified base URI must be absolute", 0); + break; + default: + /* This should be unreachable in practice. */ + zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to resolve the specified URI against the base URI", 0); + break; + } + } + goto fail; } @@ -337,10 +353,6 @@ void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t uriFreeUriMembersMmA(&uri, mm); - if (!silent) { - throw_invalid_uri_exception(); - } - return NULL; } diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index 10e90dc584a8..5dd395d22ded 100644 --- a/ext/uri/tests/004.phpt +++ b/ext/uri/tests/004.phpt @@ -29,7 +29,7 @@ var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Ca ?> --EXPECTF-- -The specified URI is malformed +The specified URI must not be empty NULL The specified URI is malformed (MissingSchemeNonRelativeUrl) NULL diff --git a/ext/uri/tests/055.phpt b/ext/uri/tests/055.phpt index 95aac9d3a8b6..1b1684d350b0 100644 --- a/ext/uri/tests/055.phpt +++ b/ext/uri/tests/055.phpt @@ -12,4 +12,4 @@ try { } ?> --EXPECT-- -The specified URI is malformed +The specified base URI must be absolute From 8bbca6c8963502f8ae685f1b8ea17e012bb93098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 18 Jul 2025 15:32:31 +0200 Subject: [PATCH 3/5] uri: Allow empty URIs for RFC3986 --- ext/uri/php_uriparser.c | 9 --------- ext/uri/tests/004.phpt | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/ext/uri/php_uriparser.c b/ext/uri/php_uriparser.c index 51922b530b0a..337e453c6d4d 100644 --- a/ext/uri/php_uriparser.c +++ b/ext/uri/php_uriparser.c @@ -296,15 +296,6 @@ void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t { UriUriA uri = {0}; - /* Empty URIs are always invalid. */ - if (ZSTR_LEN(uri_str) == 0) { - if (!silent) { - zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI must not be empty", 0); - } - - goto fail; - } - /* Parse the URI. */ if (uriParseSingleUriExMmA(&uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS) { if (!silent) { diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index 5dd395d22ded..eec6d61cea5b 100644 --- a/ext/uri/tests/004.phpt +++ b/ext/uri/tests/004.phpt @@ -29,8 +29,24 @@ var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Ca ?> --EXPECTF-- -The specified URI must not be empty -NULL +object(Uri\Rfc3986\Uri)#1 (8) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} The specified URI is malformed (MissingSchemeNonRelativeUrl) NULL object(Uri\Rfc3986\Uri)#%d (%d) { From bf7fee2d29e45ae5cb8355d18ecf43bc064cdcb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 18 Jul 2025 15:34:12 +0200 Subject: [PATCH 4/5] NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 0ff1a5bbe0a7..68cce637fc37 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ PHP NEWS . Error handling of Uri\WhatWg\Url::withHost() is fixed when the input contains a port. Now, it triggers an exception; previously, the error was silently swallowed. (Máté Kocsis) + . Support empty URIs with Uri\Rfc3986\Uri. (timwolla) 17 Jul 2025, PHP 8.5.0alpha2 From 48f18243230008850aebc521e761a3a0c5108ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 18 Jul 2025 15:36:22 +0200 Subject: [PATCH 5/5] uri: Improve ext/uri/tests/004.phpt for empty URIs --- ext/uri/tests/004.phpt | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index eec6d61cea5b..d22f52f30c48 100644 --- a/ext/uri/tests/004.phpt +++ b/ext/uri/tests/004.phpt @@ -5,12 +5,7 @@ uri --FILE-- getMessage() . "\n"; -} - +var_dump(new Uri\Rfc3986\Uri("")); var_dump(Uri\Rfc3986\Uri::parse("")); try { @@ -29,7 +24,25 @@ var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Ca ?> --EXPECTF-- -object(Uri\Rfc3986\Uri)#1 (8) { +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\Rfc3986\Uri)#%d (%d) { ["scheme"]=> NULL ["username"]=>