From cdf06e565d4ad4594dc139b715d8d6c0ec0329e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 30 Oct 2025 07:51:14 +0100 Subject: [PATCH] Fix the distinction between missing and empty username/password for RFC3986 URIs --- NEWS | 3 ++ .../parsing/userinfo_success_empty.phpt | 35 +++++++++++++++++++ .../userinfo_success_only_password.phpt | 35 +++++++++++++++++++ .../userinfo_success_only_username.phpt | 35 +++++++++++++++++++ ext/uri/uri_parser_rfc3986.c | 4 +-- 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 ext/uri/tests/rfc3986/parsing/userinfo_success_empty.phpt create mode 100644 ext/uri/tests/rfc3986/parsing/userinfo_success_only_password.phpt create mode 100644 ext/uri/tests/rfc3986/parsing/userinfo_success_only_username.phpt diff --git a/NEWS b/NEWS index ba12a18817c2c..27c0c35459f82 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,9 @@ PHP NEWS . Use the "includes credentials" rule of the WHATWG URL Standard to decide whether Uri\WhatWg\Url::getUsername() and ::getPassword() getters should return null or an empty string. (timwolla) + . Fixed the distinction between empty and missing query/fragment + components of Uri\Rfc3986\Uri. + (kocsismate) - Zip: . Fixed missing zend_release_fcall_info_cache on the following methods diff --git a/ext/uri/tests/rfc3986/parsing/userinfo_success_empty.phpt b/ext/uri/tests/rfc3986/parsing/userinfo_success_empty.phpt new file mode 100644 index 0000000000000..8b7df6c55ca00 --- /dev/null +++ b/ext/uri/tests/rfc3986/parsing/userinfo_success_empty.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\Rfc3986\Uri parsing - userinfo - empty +--EXTENSIONS-- +uri +--FILE-- +toRawString()); +var_dump($uri->toString()); + +?> +--EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(0) "" + ["password"]=> + string(0) "" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(20) "https://@example.com" +string(20) "https://@example.com" diff --git a/ext/uri/tests/rfc3986/parsing/userinfo_success_only_password.phpt b/ext/uri/tests/rfc3986/parsing/userinfo_success_only_password.phpt new file mode 100644 index 0000000000000..eb5053c10490d --- /dev/null +++ b/ext/uri/tests/rfc3986/parsing/userinfo_success_only_password.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\Rfc3986\Uri parsing - userinfo - only password +--EXTENSIONS-- +uri +--FILE-- +toRawString()); +var_dump($uri->toString()); + +?> +--EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(0) "" + ["password"]=> + string(4) "pass" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(25) "https://:pass@example.com" +string(25) "https://:pass@example.com" diff --git a/ext/uri/tests/rfc3986/parsing/userinfo_success_only_username.phpt b/ext/uri/tests/rfc3986/parsing/userinfo_success_only_username.phpt new file mode 100644 index 0000000000000..1081a0e048580 --- /dev/null +++ b/ext/uri/tests/rfc3986/parsing/userinfo_success_only_username.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\Rfc3986\Uri parsing - userinfo - only username +--EXTENSIONS-- +uri +--FILE-- +toRawString()); +var_dump($uri->toString()); + +?> +--EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(4) "user" + ["password"]=> + string(0) "" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(25) "https://user:@example.com" +string(25) "https://user:@example.com" diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index 30707fde13020..29d2025899420 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -202,7 +202,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_username_read(v } else if (c != NULL && c - uriparser_uri->userInfo.first > 0) { ZVAL_STRINGL(retval, uriparser_uri->userInfo.first, c - uriparser_uri->userInfo.first); } else { - ZVAL_NULL(retval); + ZVAL_EMPTY_STRING(retval); } } else { ZVAL_NULL(retval); @@ -221,7 +221,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_password_read(v if (c != NULL && uriparser_uri->userInfo.afterLast - c - 1 > 0) { ZVAL_STRINGL(retval, c + 1, uriparser_uri->userInfo.afterLast - c - 1); } else { - ZVAL_NULL(retval); + ZVAL_EMPTY_STRING(retval); } } else { ZVAL_NULL(retval);