Skip to content

uri: Improve exceptions for Uri\Rfc3986\Uri #19161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
71 changes: 39 additions & 32 deletions ext/uri/php_uriparser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down
45 changes: 37 additions & 8 deletions ext/uri/tests/004.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ uri
--FILE--
<?php

try {
new Uri\Rfc3986\Uri("");
} catch (Uri\InvalidUriException $e) {
echo $e->getMessage() . "\n";
}

var_dump(new Uri\Rfc3986\Uri(""));
var_dump(Uri\Rfc3986\Uri::parse(""));

try {
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion ext/uri/tests/055.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ try {
}
?>
--EXPECT--
The specified URI is malformed
The specified base URI must be absolute