Skip to content

Commit 5fdc022

Browse files
authored
uri: Improve exceptions for Uri\Rfc3986\Uri (#19161)
* uri: Streamline implementation of `uriparser_parse_uri_ex()` Avoid the use of a macro and streamline the logic. * uri: Improve exceptions for `Uri\Rfc3986\Uri` * uri: Allow empty URIs for RFC3986 * NEWS * uri: Improve ext/uri/tests/004.phpt for empty URIs
1 parent 063d795 commit 5fdc022

File tree

4 files changed

+78
-41
lines changed

4 files changed

+78
-41
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ PHP NEWS
1919
. Error handling of Uri\WhatWg\Url::withHost() is fixed when the input
2020
contains a port. Now, it triggers an exception; previously, the error
2121
was silently swallowed. (Máté Kocsis)
22+
. Support empty URIs with Uri\Rfc3986\Uri. (timwolla)
2223

2324
17 Jul 2025, PHP 8.5.0alpha2
2425

ext/uri/php_uriparser.c

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "Zend/zend_exceptions.h"
2222

2323
static void uriparser_free_uri(void *uri);
24-
static void throw_invalid_uri_exception(void);
2524

2625
static void *uriparser_malloc(UriMemoryManager *memory_manager, size_t size)
2726
{
@@ -293,51 +292,59 @@ static uriparser_uris_t *uriparser_create_uris(void)
293292
return uriparser_uris;
294293
}
295294

296-
static void throw_invalid_uri_exception(void)
297-
{
298-
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0);
299-
}
300-
301-
#define PARSE_URI(dest_uri, uri_str, uriparser_uris, silent) \
302-
do { \
303-
if (ZSTR_LEN(uri_str) == 0 || \
304-
uriParseSingleUriExMmA(dest_uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS \
305-
) { \
306-
efree(uriparser_uris); \
307-
if (!silent) { \
308-
throw_invalid_uri_exception(); \
309-
} \
310-
return NULL; \
311-
} \
312-
} while (0)
313-
314295
void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t *uriparser_base_urls, bool silent)
315296
{
316-
uriparser_uris_t *uriparser_uris = uriparser_create_uris();
297+
UriUriA uri = {0};
317298

318-
if (uriparser_base_urls == NULL) {
319-
PARSE_URI(&uriparser_uris->uri, uri_str, uriparser_uris, silent);
320-
uriMakeOwnerMmA(&uriparser_uris->uri, mm);
321-
} else {
322-
UriUriA uri;
299+
/* Parse the URI. */
300+
if (uriParseSingleUriExMmA(&uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS) {
301+
if (!silent) {
302+
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0);
303+
}
323304

324-
PARSE_URI(&uri, uri_str, uriparser_uris, silent);
305+
goto fail;
306+
}
325307

326-
if (uriAddBaseUriExMmA(&uriparser_uris->uri, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm) != URI_SUCCESS) {
327-
efree(uriparser_uris);
328-
uriFreeUriMembersMmA(&uri, mm);
308+
if (uriparser_base_urls != NULL) {
309+
UriUriA tmp = {0};
310+
311+
/* Combine the parsed URI with the base URI and store the result in 'tmp',
312+
* since the target and source URLs must be distinct. */
313+
int result = uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm);
314+
if (result != URI_SUCCESS) {
329315
if (!silent) {
330-
throw_invalid_uri_exception();
316+
switch (result) {
317+
case URI_ERROR_ADDBASE_REL_BASE:
318+
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified base URI must be absolute", 0);
319+
break;
320+
default:
321+
/* This should be unreachable in practice. */
322+
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to resolve the specified URI against the base URI", 0);
323+
break;
324+
}
331325
}
332326

333-
return NULL;
327+
goto fail;
334328
}
335329

336-
uriMakeOwnerMmA(&uriparser_uris->uri, mm);
330+
/* Store the combined URI back into 'uri'. */
337331
uriFreeUriMembersMmA(&uri, mm);
332+
uri = tmp;
338333
}
339334

335+
/* Make the resulting URI independent of the 'uri_str'. */
336+
uriMakeOwnerMmA(&uri, mm);
337+
338+
uriparser_uris_t *uriparser_uris = uriparser_create_uris();
339+
uriparser_uris->uri = uri;
340+
340341
return uriparser_uris;
342+
343+
fail:
344+
345+
uriFreeUriMembersMmA(&uri, mm);
346+
347+
return NULL;
341348
}
342349

343350
void *uriparser_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent)

ext/uri/tests/004.phpt

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@ uri
55
--FILE--
66
<?php
77

8-
try {
9-
new Uri\Rfc3986\Uri("");
10-
} catch (Uri\InvalidUriException $e) {
11-
echo $e->getMessage() . "\n";
12-
}
13-
8+
var_dump(new Uri\Rfc3986\Uri(""));
149
var_dump(Uri\Rfc3986\Uri::parse(""));
1510

1611
try {
@@ -29,8 +24,42 @@ var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Ca
2924

3025
?>
3126
--EXPECTF--
32-
The specified URI is malformed
33-
NULL
27+
object(Uri\Rfc3986\Uri)#%d (%d) {
28+
["scheme"]=>
29+
NULL
30+
["username"]=>
31+
NULL
32+
["password"]=>
33+
NULL
34+
["host"]=>
35+
NULL
36+
["port"]=>
37+
NULL
38+
["path"]=>
39+
string(0) ""
40+
["query"]=>
41+
NULL
42+
["fragment"]=>
43+
NULL
44+
}
45+
object(Uri\Rfc3986\Uri)#%d (%d) {
46+
["scheme"]=>
47+
NULL
48+
["username"]=>
49+
NULL
50+
["password"]=>
51+
NULL
52+
["host"]=>
53+
NULL
54+
["port"]=>
55+
NULL
56+
["path"]=>
57+
string(0) ""
58+
["query"]=>
59+
NULL
60+
["fragment"]=>
61+
NULL
62+
}
3463
The specified URI is malformed (MissingSchemeNonRelativeUrl)
3564
NULL
3665
object(Uri\Rfc3986\Uri)#%d (%d) {

ext/uri/tests/055.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ try {
1212
}
1313
?>
1414
--EXPECT--
15-
The specified URI is malformed
15+
The specified base URI must be absolute

0 commit comments

Comments
 (0)