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 diff --git a/ext/uri/php_uriparser.c b/ext/uri/php_uriparser.c index ba6f1ce776cb..337e453c6d4d 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,51 +292,59 @@ 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); -} - -#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(); + UriUriA uri = {0}; - if (uriparser_base_urls == NULL) { - PARSE_URI(&uriparser_uris->uri, uri_str, uriparser_uris, silent); - uriMakeOwnerMmA(&uriparser_uris->uri, mm); - } else { - UriUriA uri; + /* 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); + } - PARSE_URI(&uri, uri_str, uriparser_uris, silent); + 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 (uriparser_base_urls != NULL) { + UriUriA tmp = {0}; + + /* Combine the parsed URI with the base URI and store the result in 'tmp', + * since the target and source URLs must be distinct. */ + int result = uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm); + if (result != URI_SUCCESS) { if (!silent) { - throw_invalid_uri_exception(); + 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; + } } - return NULL; + 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); + + return NULL; } void *uriparser_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent) diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index 10e90dc584a8..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,8 +24,42 @@ var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Ca ?> --EXPECTF-- -The specified URI is malformed -NULL +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"]=> + 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) { 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