From 149015a138167c7a2a4c5d6733bcfb30329918d4 Mon Sep 17 00:00:00 2001
From: jrfnl
Date: Sat, 25 Oct 2025 05:18:40 +0200
Subject: [PATCH 01/10] [ci skip] ext/sockets: PHP 8.5 | UPGRADING: add some
missing socket constants
Refs:
* php/php-src 17440
* https://github.com/php/php-src/commit/2ea386a5163acc6c7375505313bbba4c26eefbde
close GH-20285
---
UPGRADING | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/UPGRADING b/UPGRADING
index f606585d4f05..a196757eb9ac 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -906,11 +906,11 @@ PHP 8.5 UPGRADE NOTES
. DECIMAL_COMPACT_LONG.
- OpenSSL:
- . OPENSSL_PKCS1_PSS_PADDING
- . PKCS7_NOSMIMECAP
- . PKCS7_CRLFEOL
- . PKCS7_NOCRL
- . PKCS7_NO_DUAL_CONTENT
+ . OPENSSL_PKCS1_PSS_PADDING.
+ . PKCS7_NOSMIMECAP.
+ . PKCS7_CRLFEOL.
+ . PKCS7_NOCRL.
+ . PKCS7_NO_DUAL_CONTENT.
- POSIX:
. POSIX_SC_OPEN_MAX.
@@ -924,12 +924,16 @@ PHP 8.5 UPGRADE NOTES
. TCP_REUSPORT_LB_NUMA_CURDOM (FreeBSD only).
. TCP_BBR_ALGORITHM (FreeBSD only).
. AF_PACKET (Linux only).
+ . ETH_P_IP (Linux only).
+ . ETH_P_IPV6 (Linux only).
+ . ETH_P_LOOP (Linux only).
+ . ETH_P_ALL (Linux only).
. IP_BINDANY (FreeBSD/NetBSD/OpenBSD only).
. SO_BUSY_POLL (Linux only).
. UDP_SEGMENT (Linux only).
- - SHUT_RD.
- - SHUT_WR.
- - SHUT_RDWR.
+ . SHUT_RD.
+ . SHUT_WR.
+ . SHUT_RDWR.
- Tokenizer:
. T_VOID_CAST.
From 5a7fd1de418b1379c535b3e196f1825f50877e11 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sat, 25 Oct 2025 11:15:34 +0200
Subject: [PATCH 02/10] Make bug70417.phpt less flaky
---
ext/phar/tests/tar/bug70417.phpt | 21 +++------------------
1 file changed, 3 insertions(+), 18 deletions(-)
diff --git a/ext/phar/tests/tar/bug70417.phpt b/ext/phar/tests/tar/bug70417.phpt
index 4d98a18954cc..504d7e1e387b 100644
--- a/ext/phar/tests/tar/bug70417.phpt
+++ b/ext/phar/tests/tar/bug70417.phpt
@@ -3,32 +3,17 @@ Bug #70417 (PharData::compress() doesn't close temp file)
--EXTENSIONS--
phar
zlib
---SKIPIF--
-
--FILE--
/dev/null', $out); // Note: valgrind can produce false positives for /usr/bin/lsof
- return count($out);
-}
$filename = __DIR__ . '/bug70417.tar';
@unlink("$filename.gz");
-$openFiles1 = countOpenFiles();
+$resBefore = count(get_resources());
$arch = new PharData($filename);
$arch->addFromString('foo', 'bar');
$arch->compress(Phar::GZ);
unset($arch);
-$openFiles2 = countOpenFiles();
-var_dump($openFiles1 === $openFiles2);
+$resAfter = count(get_resources());
+var_dump($resBefore === $resAfter);
?>
--CLEAN--
Date: Sat, 25 Oct 2025 11:15:34 +0200
Subject: [PATCH 03/10] Make bug70417.phpt less flaky
Closes GH-20287.
---
ext/phar/tests/tar/bug70417.phpt | 21 +++------------------
1 file changed, 3 insertions(+), 18 deletions(-)
diff --git a/ext/phar/tests/tar/bug70417.phpt b/ext/phar/tests/tar/bug70417.phpt
index 4d98a18954cc..504d7e1e387b 100644
--- a/ext/phar/tests/tar/bug70417.phpt
+++ b/ext/phar/tests/tar/bug70417.phpt
@@ -3,32 +3,17 @@ Bug #70417 (PharData::compress() doesn't close temp file)
--EXTENSIONS--
phar
zlib
---SKIPIF--
-
--FILE--
/dev/null', $out); // Note: valgrind can produce false positives for /usr/bin/lsof
- return count($out);
-}
$filename = __DIR__ . '/bug70417.tar';
@unlink("$filename.gz");
-$openFiles1 = countOpenFiles();
+$resBefore = count(get_resources());
$arch = new PharData($filename);
$arch->addFromString('foo', 'bar');
$arch->compress(Phar::GZ);
unset($arch);
-$openFiles2 = countOpenFiles();
-var_dump($openFiles1 === $openFiles2);
+$resAfter = count(get_resources());
+var_dump($resBefore === $resAfter);
?>
--CLEAN--
Date: Fri, 24 Oct 2025 21:17:00 +0200
Subject: [PATCH 04/10] Fix GH-20281: \Dom\Document::getElementById() is
inconsistent after nodes are removed
This worked for non-parsed elements already, but not for elements where
xmlAddID() returns early due to the ID already existing.
In that case what was missing is marking the attribute as an ID.
Closes GH-20283.
---
NEWS | 2 ++
ext/dom/html5_parser.c | 5 ++++-
.../tests/modern/html/interactions/gh20281.phpt | 15 +++++++++++++++
3 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 ext/dom/tests/modern/html/interactions/gh20281.phpt
diff --git a/NEWS b/NEWS
index 1a7af0a2eb1b..35f06bd93b24 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@ PHP NEWS
- DOM:
. Partially fixed bug GH-16317 (DOM classes do not allow
__debugInfo() overrides to work). (nielsdos)
+ . Fixed bug GH-20281 (\Dom\Document::getElementById() is inconsistent
+ after nodes are removed). (nielsdos)
- Exif:
. Fix possible memory leak when tag is empty. (nielsdos)
diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c
index f1dc2db53b25..d5fe3d5c2773 100644
--- a/ext/dom/html5_parser.c
+++ b/ext/dom/html5_parser.c
@@ -268,7 +268,10 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert(
/* xmlIsID does some other stuff too that is irrelevant here. */
if (local_name_length == 2 && local_name[0] == 'i' && local_name[1] == 'd' && attr->node.ns == LXB_NS_HTML) {
- xmlAddID(NULL, lxml_doc, value, lxml_attr);
+ if (xmlAddID(NULL, lxml_doc, value, lxml_attr) == 0) {
+ /* If the ID already exists, the ID attribute still needs to be marked as an ID. */
+ lxml_attr->atype = XML_ATTRIBUTE_ID;
+ }
}
/* libxml2 doesn't support line numbers on this anyway, it derives them instead, so don't bother */
diff --git a/ext/dom/tests/modern/html/interactions/gh20281.phpt b/ext/dom/tests/modern/html/interactions/gh20281.phpt
new file mode 100644
index 000000000000..324c5a275633
--- /dev/null
+++ b/ext/dom/tests/modern/html/interactions/gh20281.phpt
@@ -0,0 +1,15 @@
+--TEST--
+GH-20281 (\Dom\Document::getElementById() is inconsistent after nodes are removed)
+--EXTENSIONS--
+dom
+--CREDITS--
+cscott
+--FILE--
+b
c
', LIBXML_NOERROR);
+$p = $d->getElementById('a');
+$p->remove();
+echo $d->getElementById('a')->textContent, "\n";
+?>
+--EXPECT--
+c
From 0af4f27981025dd27a35bac946862ef83fe8ab52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Sat, 25 Oct 2025 14:38:52 +0200
Subject: [PATCH 05/10] Fix GH-20274 SoapClient::__doRequest undocumented
backwards incompatible in PHP 8.5 (#20278)
---
NEWS | 2 +-
UPGRADING | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/NEWS b/NEWS
index dd46f907d09e..23933494c825 100644
--- a/NEWS
+++ b/NEWS
@@ -354,7 +354,7 @@ PHP NEWS
. Added support for partitioned cookies. (nielsdos)
- SOAP:
- . Added support for configuring the URI parser for SoapClient::_doRequest()
+ . Added support for configuring the URI parser for SoapClient::__doRequest()
as described in https://wiki.php.net/rfc/url_parsing_api#plugability.
(kocsismate)
diff --git a/UPGRADING b/UPGRADING
index a196757eb9ac..b0f3c9155b31 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -170,6 +170,10 @@ PHP 8.5 UPGRADE NOTES
precision previously incorrectly reset the precision instead of treating
it as a precision of 0. See GH-18897.
+- SOAP:
+ . SoapClient::__doRequest() expects a new, optional $uriParserClass parameter
+ as described in https://wiki.php.net/rfc/url_parsing_api#plugability.
+
========================================
2. New Features
========================================
From 27bc7c0e1294eb0ce87f071acd42f43f01877d61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Sat, 25 Oct 2025 14:46:06 +0200
Subject: [PATCH 06/10] Reorganize ext/uri tests - withers (#19970)
---
.../modification/fragment_error_reserved.phpt | 18 ++++++++++++++
.../modification/fragment_error_unicode.phpt | 18 ++++++++++++++
.../modification/fragment_success_empty.phpt | 23 ++++++++++++++++++
.../fragment_success_encoded.phpt | 23 ++++++++++++++++++
.../fragment_success_existing.phpt | 23 ++++++++++++++++++
.../fragment_success_unset_existing.phpt | 23 ++++++++++++++++++
.../fragment_success_unset_non_existent.phpt | 23 ++++++++++++++++++
.../modification/host_error_reserved.phpt | 18 ++++++++++++++
.../modification/host_success_empty.phpt | 23 ++++++++++++++++++
.../modification/host_success_encoded.phpt | 23 ++++++++++++++++++
.../modification/host_success_existing.phpt | 23 ++++++++++++++++++
.../modification/host_success_ip_future.phpt | 23 ++++++++++++++++++
.../modification/host_success_ipv4.phpt | 23 ++++++++++++++++++
.../modification/host_success_ipv6.phpt | 23 ++++++++++++++++++
.../modification/host_success_new.phpt | 23 ++++++++++++++++++
.../host_success_unset_existing.phpt | 23 ++++++++++++++++++
.../host_success_unset_non_existent.phpt | 23 ++++++++++++++++++
.../modification/path_error_reserved.phpt | 18 ++++++++++++++
.../modification/path_error_unicode.phpt | 18 ++++++++++++++
.../path_error_without_leading_slash.phpt | 18 ++++++++++++++
.../modification/path_success_email.phpt | 23 ++++++++++++++++++
.../modification/path_success_empty.phpt | 23 ++++++++++++++++++
.../modification/path_success_encoded.phpt | 23 ++++++++++++++++++
.../modification/path_success_existing.phpt | 23 ++++++++++++++++++
.../modification/port_error_negative.phpt | 18 ++++++++++++++
.../modification/port_success_existing.phpt | 19 +++++++++++++++
.../modification/port_success_new.phpt | 19 +++++++++++++++
.../port_success_unset_existing.phpt | 19 +++++++++++++++
.../port_success_unset_non_existent.phpt | 19 +++++++++++++++
.../modification/query_error_reserved.phpt | 18 ++++++++++++++
.../modification/query_error_unicode.phpt | 18 ++++++++++++++
...ry_success_context_sensitive_reserved.phpt | 23 ++++++++++++++++++
.../modification/query_success_empty.phpt | 23 ++++++++++++++++++
.../modification/query_success_encoded.phpt | 23 ++++++++++++++++++
.../modification/query_success_existing.phpt | 23 ++++++++++++++++++
.../query_success_unset_existing.phpt | 23 ++++++++++++++++++
.../query_success_unset_non_existent.phpt | 23 ++++++++++++++++++
.../modification/roundtrip_special_case1.phpt | 24 +++++++++++++++++++
.../modification/roundtrip_special_case2.phpt | 20 ++++++++++++++++
.../modification/roundtrip_special_case3.phpt | 20 ++++++++++++++++
.../modification/scheme_error_empty.phpt | 18 ++++++++++++++
.../modification/scheme_error_encoded.phpt | 18 ++++++++++++++
.../modification/scheme_error_reserved.phpt | 18 ++++++++++++++
.../modification/scheme_success_basic.phpt | 23 ++++++++++++++++++
.../scheme_success_unset_existing.phpt | 23 ++++++++++++++++++
.../scheme_success_unset_non_existent.phpt | 23 ++++++++++++++++++
.../modification/userinfo_error_reserved.phpt | 18 ++++++++++++++
.../modification/userinfo_success_empty.phpt | 19 +++++++++++++++
.../userinfo_success_encoded.phpt | 23 ++++++++++++++++++
.../userinfo_success_existing.phpt | 23 ++++++++++++++++++
.../modification/userinfo_success_new.phpt | 23 ++++++++++++++++++
.../userinfo_success_unset_existing.phpt | 23 ++++++++++++++++++
.../userinfo_success_unset_non_existent.phpt | 23 ++++++++++++++++++
.../fragment_success_auto_encode.phpt | 19 +++++++++++++++
.../modification/fragment_success_empty.phpt | 19 +++++++++++++++
.../fragment_success_encoded.phpt | 19 +++++++++++++++
.../fragment_success_existing.phpt | 19 +++++++++++++++
.../fragment_success_hashmark.phpt | 19 +++++++++++++++
.../fragment_success_unicode.phpt | 19 +++++++++++++++
.../fragment_success_unset_existing.phpt | 19 +++++++++++++++
.../fragment_success_unset_non_existent.phpt | 19 +++++++++++++++
...ragment_success_with_leading_hashmark.phpt | 19 +++++++++++++++
.../whatwg/modification/host_error_empty.phpt | 18 ++++++++++++++
...rror_forbidden_host_codepoint_opaque1.phpt | 18 ++++++++++++++
...rror_forbidden_host_codepoint_opaque2.phpt | 17 +++++++++++++
...rror_forbidden_host_codepoint_opaque3.phpt | 17 +++++++++++++
...ror_forbidden_host_codepoint_special1.phpt | 18 ++++++++++++++
...ror_forbidden_host_codepoint_special2.phpt | 18 ++++++++++++++
...ror_forbidden_host_codepoint_special3.phpt | 18 ++++++++++++++
.../host_error_unset_existing.phpt | 18 ++++++++++++++
.../modification/host_success_encoded.phpt | 19 +++++++++++++++
.../modification/host_success_existing.phpt | 19 +++++++++++++++
.../modification/host_success_idna.phpt | 23 ++++++++++++++++++
.../modification/host_success_ipv4.phpt | 19 +++++++++++++++
.../modification/host_success_ipv6.phpt | 19 +++++++++++++++
.../password_success_auto_encoded.phpt | 19 +++++++++++++++
.../modification/password_success_empty.phpt | 19 +++++++++++++++
.../password_success_encoded.phpt | 19 +++++++++++++++
.../password_success_existing.phpt | 19 +++++++++++++++
.../modification/password_success_new.phpt | 19 +++++++++++++++
.../password_success_unset_existing.phpt | 19 +++++++++++++++
.../password_success_unset_non_existent1.phpt | 19 +++++++++++++++
.../password_success_unset_non_existent2.phpt | 19 +++++++++++++++
.../path_success_auto_encoded.phpt | 19 +++++++++++++++
.../modification/path_success_empty.phpt | 19 +++++++++++++++
.../modification/path_success_encoded.phpt | 19 +++++++++++++++
.../modification/path_success_existing.phpt | 19 +++++++++++++++
.../modification/path_success_unicode.phpt | 18 ++++++++++++++
.../path_success_without_leading_slash.phpt | 19 +++++++++++++++
.../modification/port_error_negative.phpt | 18 ++++++++++++++
.../modification/port_error_too_large.phpt | 18 ++++++++++++++
.../modification/port_success_existing.phpt | 19 +++++++++++++++
.../whatwg/modification/port_success_new.phpt | 19 +++++++++++++++
.../modification/port_success_special1.phpt | 19 +++++++++++++++
.../modification/port_success_special2.phpt | 19 +++++++++++++++
.../port_success_unset_existing.phpt | 19 +++++++++++++++
.../port_success_unset_non_existent.phpt | 19 +++++++++++++++
.../query_success_auto_encoded.phpt | 19 +++++++++++++++
...ry_success_context_sensitive_reserved.phpt | 19 +++++++++++++++
.../modification/query_success_empty.phpt | 19 +++++++++++++++
.../modification/query_success_encoded.phpt | 19 +++++++++++++++
.../modification/query_success_existing.phpt | 19 +++++++++++++++
.../query_success_question_mark.phpt | 19 +++++++++++++++
.../modification/query_success_unicode.phpt | 19 +++++++++++++++
.../query_success_unset_existing.phpt | 19 +++++++++++++++
.../query_success_unset_non_existent.phpt | 19 +++++++++++++++
...ry_success_with_leading_question_mark.phpt | 19 +++++++++++++++
.../modification/scheme_error_empty.phpt | 18 ++++++++++++++
.../modification/scheme_error_encoded.phpt | 18 ++++++++++++++
.../modification/scheme_error_invalid.phpt | 18 ++++++++++++++
.../modification/scheme_success_basic.phpt | 19 +++++++++++++++
.../modification/scheme_success_colon.phpt | 19 +++++++++++++++
.../modification/scheme_success_full.phpt | 19 +++++++++++++++
.../username_success_auto_encoded.phpt | 19 +++++++++++++++
.../username_success_existing.phpt | 19 +++++++++++++++
.../modification/username_success_new.phpt | 19 +++++++++++++++
.../username_success_unset_existing.phpt | 19 +++++++++++++++
.../username_success_unset_non_existent1.phpt | 19 +++++++++++++++
.../username_success_unset_non_existent2.phpt | 19 +++++++++++++++
119 files changed, 2371 insertions(+)
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_error_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_error_unicode.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_success_empty.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_success_encoded.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_success_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_success_unset_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/fragment_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_error_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_empty.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_encoded.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_ip_future.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_ipv4.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_ipv6.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_new.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/host_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_error_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_error_unicode.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_error_without_leading_slash.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_success_email.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_success_empty.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_success_encoded.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/path_success_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/port_error_negative.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/port_success_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/port_success_new.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/port_success_unset_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/port_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_error_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_error_unicode.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_success_context_sensitive_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_success_empty.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_success_encoded.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_success_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_success_unset_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/query_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/roundtrip_special_case1.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/roundtrip_special_case2.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/roundtrip_special_case3.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/scheme_error_empty.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/scheme_error_encoded.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/scheme_error_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/scheme_success_basic.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/scheme_success_unset_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/scheme_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_error_reserved.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_success_empty.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_success_encoded.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_success_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_success_new.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_success_unset_existing.phpt
create mode 100644 ext/uri/tests/rfc3986/modification/userinfo_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_auto_encode.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_empty.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_unicode.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_unset_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_with_leading_hashmark.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_empty.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque1.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque2.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque3.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special1.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special2.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special3.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_error_unset_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_success_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_success_idna.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_success_ipv4.phpt
create mode 100644 ext/uri/tests/whatwg/modification/host_success_ipv6.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_auto_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_empty.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_new.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_unset_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_unset_non_existent1.phpt
create mode 100644 ext/uri/tests/whatwg/modification/password_success_unset_non_existent2.phpt
create mode 100644 ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/path_success_empty.phpt
create mode 100644 ext/uri/tests/whatwg/modification/path_success_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/path_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/path_success_unicode.phpt
create mode 100644 ext/uri/tests/whatwg/modification/path_success_without_leading_slash.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_error_negative.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_error_too_large.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_success_new.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_success_special1.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_success_special2.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_success_unset_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/port_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_auto_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_context_sensitive_reserved.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_empty.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_question_mark.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_unicode.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_unset_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_unset_non_existent.phpt
create mode 100644 ext/uri/tests/whatwg/modification/query_success_with_leading_question_mark.phpt
create mode 100644 ext/uri/tests/whatwg/modification/scheme_error_empty.phpt
create mode 100644 ext/uri/tests/whatwg/modification/scheme_error_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/scheme_error_invalid.phpt
create mode 100644 ext/uri/tests/whatwg/modification/scheme_success_basic.phpt
create mode 100644 ext/uri/tests/whatwg/modification/scheme_success_colon.phpt
create mode 100644 ext/uri/tests/whatwg/modification/scheme_success_full.phpt
create mode 100644 ext/uri/tests/whatwg/modification/username_success_auto_encoded.phpt
create mode 100644 ext/uri/tests/whatwg/modification/username_success_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/username_success_new.phpt
create mode 100644 ext/uri/tests/whatwg/modification/username_success_unset_existing.phpt
create mode 100644 ext/uri/tests/whatwg/modification/username_success_unset_non_existent1.phpt
create mode 100644 ext/uri/tests/whatwg/modification/username_success_unset_non_existent2.phpt
diff --git a/ext/uri/tests/rfc3986/modification/fragment_error_reserved.phpt b/ext/uri/tests/rfc3986/modification/fragment_error_reserved.phpt
new file mode 100644
index 000000000000..4b3c3dd00a99
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_error_reserved.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - reserved characters
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("#fragment");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified fragment is malformed
diff --git a/ext/uri/tests/rfc3986/modification/fragment_error_unicode.phpt b/ext/uri/tests/rfc3986/modification/fragment_error_unicode.phpt
new file mode 100644
index 000000000000..bbb1e2ffe872
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_error_unicode.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - unicode characters
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("ő");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified fragment is malformed
diff --git a/ext/uri/tests/rfc3986/modification/fragment_success_empty.phpt b/ext/uri/tests/rfc3986/modification/fragment_success_empty.phpt
new file mode 100644
index 000000000000..f0a05d5bb5bb
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_success_empty.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("");
+
+var_dump($uri1->getRawFragment());
+var_dump($uri2->getRawFragment());
+var_dump($uri2->toRawString());
+var_dump($uri2->getFragment());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(0) ""
+string(20) "https://example.com#"
+string(0) ""
+string(20) "https://example.com#"
diff --git a/ext/uri/tests/rfc3986/modification/fragment_success_encoded.phpt b/ext/uri/tests/rfc3986/modification/fragment_success_encoded.phpt
new file mode 100644
index 000000000000..b1b1081ff81c
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_success_encoded.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("foo%3db%61r"); // foo=bar
+
+var_dump($uri1->getRawFragment());
+var_dump($uri2->getRawFragment());
+var_dump($uri2->toRawString());
+var_dump($uri2->getFragment());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(11) "foo%3db%61r"
+string(31) "https://example.com#foo%3db%61r"
+string(9) "foo%3Dbar"
+string(29) "https://example.com#foo%3Dbar"
diff --git a/ext/uri/tests/rfc3986/modification/fragment_success_existing.phpt b/ext/uri/tests/rfc3986/modification/fragment_success_existing.phpt
new file mode 100644
index 000000000000..fe534e846504
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_success_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("bar");
+
+var_dump($uri1->getRawFragment());
+var_dump($uri2->getRawFragment());
+var_dump($uri2->toRawString());
+var_dump($uri2->getFragment());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(3) "foo"
+string(3) "bar"
+string(23) "https://example.com#bar"
+string(3) "bar"
+string(23) "https://example.com#bar"
diff --git a/ext/uri/tests/rfc3986/modification/fragment_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/fragment_success_unset_existing.phpt
new file mode 100644
index 000000000000..7532c35e0fe7
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_success_unset_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withFragment(null);
+
+var_dump($uri1->getRawFragment());
+var_dump($uri2->getRawFragment());
+var_dump($uri2->toRawString());
+var_dump($uri2->getFragment());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(3) "foo"
+NULL
+string(19) "https://example.com"
+NULL
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/fragment_success_unset_non_existent.phpt b/ext/uri/tests/rfc3986/modification/fragment_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..1c6fa898dde3
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/fragment_success_unset_non_existent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - fragment - unsetting not-existent
+--EXTENSIONS--
+uri
+--FILE--
+withFragment(null);
+
+var_dump($uri1->getRawFragment());
+var_dump($uri2->getRawFragment());
+var_dump($uri2->toRawString());
+var_dump($uri2->getFragment());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(19) "https://example.com"
+NULL
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/host_error_reserved.phpt b/ext/uri/tests/rfc3986/modification/host_error_reserved.phpt
new file mode 100644
index 000000000000..81d60bc2e400
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_error_reserved.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - reserved characters
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex#mple.com");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed
diff --git a/ext/uri/tests/rfc3986/modification/host_success_empty.phpt b/ext/uri/tests/rfc3986/modification/host_success_empty.phpt
new file mode 100644
index 000000000000..b337674841c6
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_empty.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withHost("");
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(0) ""
+string(8) "https://"
+string(0) ""
+string(8) "https://"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_encoded.phpt b/ext/uri/tests/rfc3986/modification/host_success_encoded.phpt
new file mode 100644
index 000000000000..d9db6003b157
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_encoded.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withHost("%65xample.net"); // example.net
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(13) "%65xample.net"
+string(21) "https://%65xample.net"
+string(11) "example.net"
+string(19) "https://example.net"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_existing.phpt b/ext/uri/tests/rfc3986/modification/host_success_existing.phpt
new file mode 100644
index 000000000000..f9d3da987abc
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withHost("example.net");
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(11) "example.net"
+string(19) "https://example.net"
+string(11) "example.net"
+string(19) "https://example.net"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_ip_future.phpt b/ext/uri/tests/rfc3986/modification/host_success_ip_future.phpt
new file mode 100644
index 000000000000..004210000cf4
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_ip_future.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - IP future address
+--EXTENSIONS--
+uri
+--FILE--
+withHost("[vF.addr]");
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(9) "[vF.addr]"
+string(17) "https://[vF.addr]"
+string(9) "[vf.addr]"
+string(17) "https://[vf.addr]"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_ipv4.phpt b/ext/uri/tests/rfc3986/modification/host_success_ipv4.phpt
new file mode 100644
index 000000000000..850a32281aba
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_ipv4.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - IPv4 address
+--EXTENSIONS--
+uri
+--FILE--
+withHost("192.168.0.1");
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(11) "192.168.0.1"
+string(19) "https://192.168.0.1"
+string(11) "192.168.0.1"
+string(19) "https://192.168.0.1"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_ipv6.phpt b/ext/uri/tests/rfc3986/modification/host_success_ipv6.phpt
new file mode 100644
index 000000000000..475088840159
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_ipv6.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - IPv6 address
+--EXTENSIONS--
+uri
+--FILE--
+withHost("[2001:0db8:3333:4444:5555:6666:7777:8888]");
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(41) "[2001:0db8:3333:4444:5555:6666:7777:8888]"
+string(49) "https://[2001:0db8:3333:4444:5555:6666:7777:8888]"
+string(41) "[2001:0db8:3333:4444:5555:6666:7777:8888]"
+string(49) "https://[2001:0db8:3333:4444:5555:6666:7777:8888]"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_new.phpt b/ext/uri/tests/rfc3986/modification/host_success_new.phpt
new file mode 100644
index 000000000000..326616140078
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_new.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - adding a new one
+--EXTENSIONS--
+uri
+--FILE--
+withHost("example.com");
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(11) "example.com"
+string(21) "//example.com/foo/bar"
+string(11) "example.com"
+string(21) "//example.com/foo/bar"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt
new file mode 100644
index 000000000000..521b6397ec45
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withHost(null);
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+NULL
+string(7) "https:/"
+NULL
+string(7) "https:/"
diff --git a/ext/uri/tests/rfc3986/modification/host_success_unset_non_existent.phpt b/ext/uri/tests/rfc3986/modification/host_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..b1793a31413a
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/host_success_unset_non_existent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withHost(null);
+
+var_dump($uri1->getRawHost());
+var_dump($uri2->getRawHost());
+var_dump($uri2->toRawString());
+var_dump($uri2->getHost());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(8) "/foo/bar"
+NULL
+string(8) "/foo/bar"
diff --git a/ext/uri/tests/rfc3986/modification/path_error_reserved.phpt b/ext/uri/tests/rfc3986/modification/path_error_reserved.phpt
new file mode 100644
index 000000000000..2da84e2689b3
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_error_reserved.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - reserved characters
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/[foo]");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified path is malformed
diff --git a/ext/uri/tests/rfc3986/modification/path_error_unicode.phpt b/ext/uri/tests/rfc3986/modification/path_error_unicode.phpt
new file mode 100644
index 000000000000..9335132dad56
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_error_unicode.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - unicode characters
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/ő");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified path is malformed
diff --git a/ext/uri/tests/rfc3986/modification/path_error_without_leading_slash.phpt b/ext/uri/tests/rfc3986/modification/path_error_without_leading_slash.phpt
new file mode 100644
index 000000000000..476e4ec873c2
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_error_without_leading_slash.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - without leading slash
+--EXTENSIONS--
+uri
+--FILE--
+withPath("foo");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified path is malformed
diff --git a/ext/uri/tests/rfc3986/modification/path_success_email.phpt b/ext/uri/tests/rfc3986/modification/path_success_email.phpt
new file mode 100644
index 000000000000..58f5ab4baa6d
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_success_email.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - using an email format
+--EXTENSIONS--
+uri
+--FILE--
+withPath("john.doe@example.com");
+
+var_dump($uri1->getRawPath());
+var_dump($uri2->getRawPath());
+var_dump($uri2->toRawString());
+var_dump($uri2->getPath());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(0) ""
+string(20) "john.doe@example.com"
+string(20) "john.doe@example.com"
+string(20) "john.doe@example.com"
+string(20) "john.doe@example.com"
diff --git a/ext/uri/tests/rfc3986/modification/path_success_empty.phpt b/ext/uri/tests/rfc3986/modification/path_success_empty.phpt
new file mode 100644
index 000000000000..a67eeae44eed
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_success_empty.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withPath("");
+
+var_dump($uri1->getRawPath());
+var_dump($uri2->getRawPath());
+var_dump($uri2->toRawString());
+var_dump($uri2->getPath());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(0) ""
+string(0) ""
+string(19) "https://example.com"
+string(0) ""
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/path_success_encoded.phpt b/ext/uri/tests/rfc3986/modification/path_success_encoded.phpt
new file mode 100644
index 000000000000..05b62cafe250
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_success_encoded.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/foo%2Fb%61r"); // /foo/bar
+
+var_dump($uri1->getRawPath());
+var_dump($uri2->getRawPath());
+var_dump($uri2->toRawString());
+var_dump($uri2->getPath());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(0) ""
+string(12) "/foo%2Fb%61r"
+string(31) "https://example.com/foo%2Fb%61r"
+string(10) "/foo%2Fbar"
+string(29) "https://example.com/foo%2Fbar"
diff --git a/ext/uri/tests/rfc3986/modification/path_success_existing.phpt b/ext/uri/tests/rfc3986/modification/path_success_existing.phpt
new file mode 100644
index 000000000000..6dafbc0a3ff8
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/path_success_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - path - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/baz");
+
+var_dump($uri1->getRawPath());
+var_dump($uri2->getRawPath());
+var_dump($uri2->toRawString());
+var_dump($uri2->getPath());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(8) "/foo/bar"
+string(4) "/baz"
+string(23) "https://example.com/baz"
+string(4) "/baz"
+string(23) "https://example.com/baz"
diff --git a/ext/uri/tests/rfc3986/modification/port_error_negative.phpt b/ext/uri/tests/rfc3986/modification/port_error_negative.phpt
new file mode 100644
index 000000000000..598475b1e184
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/port_error_negative.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - host - too small number
+--EXTENSIONS--
+uri
+--FILE--
+withPort(-1);
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified port is malformed
diff --git a/ext/uri/tests/rfc3986/modification/port_success_existing.phpt b/ext/uri/tests/rfc3986/modification/port_success_existing.phpt
new file mode 100644
index 000000000000..8451f1bcf469
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/port_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - port - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withPort(443);
+
+var_dump($uri1->getPort());
+var_dump($uri2->getPort());
+var_dump($uri2->toRawString());
+
+?>
+--EXPECT--
+int(80)
+int(443)
+string(23) "https://example.com:443"
diff --git a/ext/uri/tests/rfc3986/modification/port_success_new.phpt b/ext/uri/tests/rfc3986/modification/port_success_new.phpt
new file mode 100644
index 000000000000..36ccea04a949
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/port_success_new.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - port - adding a new one
+--EXTENSIONS--
+uri
+--FILE--
+withPort(443);
+
+var_dump($uri1->getPort());
+var_dump($uri2->getPort());
+var_dump($uri2->toRawString());
+
+?>
+--EXPECT--
+NULL
+int(443)
+string(23) "https://example.com:443"
diff --git a/ext/uri/tests/rfc3986/modification/port_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/port_success_unset_existing.phpt
new file mode 100644
index 000000000000..574af3f6e3ee
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/port_success_unset_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - port - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withPort(null);
+
+var_dump($uri1->getPort());
+var_dump($uri2->getPort());
+var_dump($uri2->toRawString());
+
+?>
+--EXPECT--
+int(80)
+NULL
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/port_success_unset_non_existent.phpt b/ext/uri/tests/rfc3986/modification/port_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..a3fbdc5e8742
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/port_success_unset_non_existent.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - port - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withPort(null);
+
+var_dump($uri2->getPort());
+var_dump($uri2->getPort());
+var_dump($uri2->toRawString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(17) "ftp://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/query_error_reserved.phpt b/ext/uri/tests/rfc3986/modification/query_error_reserved.phpt
new file mode 100644
index 000000000000..f09e867a0210
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_error_reserved.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - reserved characters
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("#foo");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified query is malformed
diff --git a/ext/uri/tests/rfc3986/modification/query_error_unicode.phpt b/ext/uri/tests/rfc3986/modification/query_error_unicode.phpt
new file mode 100644
index 000000000000..c8f510059742
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_error_unicode.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - unicode characters
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("ő");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified query is malformed
diff --git a/ext/uri/tests/rfc3986/modification/query_success_context_sensitive_reserved.phpt b/ext/uri/tests/rfc3986/modification/query_success_context_sensitive_reserved.phpt
new file mode 100644
index 000000000000..d40a7f7e09ef
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_success_context_sensitive_reserved.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - context-sensitive reserved character
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("?foo=bar");
+
+var_dump($uri1->getRawQuery());
+var_dump($uri2->getRawQuery());
+var_dump($uri2->toRawString());
+var_dump($uri2->getQuery());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(8) "?foo=bar"
+string(28) "https://example.com??foo=bar"
+string(8) "?foo=bar"
+string(28) "https://example.com??foo=bar"
diff --git a/ext/uri/tests/rfc3986/modification/query_success_empty.phpt b/ext/uri/tests/rfc3986/modification/query_success_empty.phpt
new file mode 100644
index 000000000000..e668b44df9b3
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_success_empty.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("");
+
+var_dump($uri1->getRawQuery());
+var_dump($uri2->getRawQuery());
+var_dump($uri2->toRawString());
+var_dump($uri2->getQuery());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(0) ""
+string(20) "https://example.com?"
+string(0) ""
+string(20) "https://example.com?"
diff --git a/ext/uri/tests/rfc3986/modification/query_success_encoded.phpt b/ext/uri/tests/rfc3986/modification/query_success_encoded.phpt
new file mode 100644
index 000000000000..7fd9707220d8
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_success_encoded.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("foo%3dbar"); // foo=bar
+
+var_dump($uri1->getRawQuery());
+var_dump($uri2->getRawQuery());
+var_dump($uri2->toRawString());
+var_dump($uri2->getQuery());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(9) "foo%3dbar"
+string(29) "https://example.com?foo%3dbar"
+string(9) "foo%3Dbar"
+string(29) "https://example.com?foo%3Dbar"
diff --git a/ext/uri/tests/rfc3986/modification/query_success_existing.phpt b/ext/uri/tests/rfc3986/modification/query_success_existing.phpt
new file mode 100644
index 000000000000..b5af7feee250
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_success_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("foo=bar&baz=qux");
+
+var_dump($uri1->getRawQuery());
+var_dump($uri2->getRawQuery());
+var_dump($uri2->toRawString());
+var_dump($uri2->getQuery());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(7) "foo=bar"
+string(15) "foo=bar&baz=qux"
+string(35) "https://example.com?foo=bar&baz=qux"
+string(15) "foo=bar&baz=qux"
+string(35) "https://example.com?foo=bar&baz=qux"
diff --git a/ext/uri/tests/rfc3986/modification/query_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/query_success_unset_existing.phpt
new file mode 100644
index 000000000000..89d130eedc27
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_success_unset_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withQuery(null);
+
+var_dump($uri1->getRawQuery());
+var_dump($uri2->getRawQuery());
+var_dump($uri2->toRawString());
+var_dump($uri2->getQuery());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(7) "foo=bar"
+NULL
+string(19) "https://example.com"
+NULL
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/query_success_unset_non_existent.phpt b/ext/uri/tests/rfc3986/modification/query_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..d9dfcd208310
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/query_success_unset_non_existent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - query - unsetting not-existent
+--EXTENSIONS--
+uri
+--FILE--
+withQuery(null);
+
+var_dump($uri1->getRawQuery());
+var_dump($uri2->getRawQuery());
+var_dump($uri2->toRawString());
+var_dump($uri2->getQuery());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(19) "https://example.com"
+NULL
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/roundtrip_special_case1.phpt b/ext/uri/tests/rfc3986/modification/roundtrip_special_case1.phpt
new file mode 100644
index 000000000000..4349764d4dd1
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/roundtrip_special_case1.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification when roundtripping is not guaranteed - case 1
+--EXTENSIONS--
+uri
+--FILE--
+withHost("host");
+$uri2 = $uri2->withHost(null);
+
+var_dump($uri1->getRawPath());
+var_dump($uri2->getRawPath());
+var_dump($uri2->toRawString());
+var_dump($uri2->getPath());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(5) "path1"
+string(6) "/path1"
+string(6) "/path1"
+string(6) "/path1"
+string(6) "/path1"
diff --git a/ext/uri/tests/rfc3986/modification/roundtrip_special_case2.phpt b/ext/uri/tests/rfc3986/modification/roundtrip_special_case2.phpt
new file mode 100644
index 000000000000..624abf2fc49c
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/roundtrip_special_case2.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification when roundtripping is not guaranteed - case 2
+--EXTENSIONS--
+uri
+--FILE--
+withScheme(null);
+$uri2 = $uri2->withScheme("scheme");
+
+var_dump($uri1->toRawString());
+var_dump($uri2->toRawString());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(13) "scheme:path1:"
+string(15) "scheme:./path1:"
+string(13) "scheme:path1:"
diff --git a/ext/uri/tests/rfc3986/modification/roundtrip_special_case3.phpt b/ext/uri/tests/rfc3986/modification/roundtrip_special_case3.phpt
new file mode 100644
index 000000000000..7d6e1e4d0489
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/roundtrip_special_case3.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification when roundtripping is not guaranteed - case 3
+--EXTENSIONS--
+uri
+--FILE--
+withHost(null);
+$uri2 = $uri2->withHost("host");
+
+var_dump($uri1->toRawString());
+var_dump($uri2->toRawString());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(12) "//host//path"
+string(14) "//host/.//path"
+string(12) "//host//path"
diff --git a/ext/uri/tests/rfc3986/modification/scheme_error_empty.phpt b/ext/uri/tests/rfc3986/modification/scheme_error_empty.phpt
new file mode 100644
index 000000000000..477be1d2331c
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/scheme_error_empty.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - scheme - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified scheme is malformed
diff --git a/ext/uri/tests/rfc3986/modification/scheme_error_encoded.phpt b/ext/uri/tests/rfc3986/modification/scheme_error_encoded.phpt
new file mode 100644
index 000000000000..683f46cc42a8
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/scheme_error_encoded.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - scheme - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("http%73");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified scheme is malformed
diff --git a/ext/uri/tests/rfc3986/modification/scheme_error_reserved.phpt b/ext/uri/tests/rfc3986/modification/scheme_error_reserved.phpt
new file mode 100644
index 000000000000..dd3bcc02f83e
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/scheme_error_reserved.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - scheme - reserved characters
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("https:");
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified scheme is malformed
diff --git a/ext/uri/tests/rfc3986/modification/scheme_success_basic.phpt b/ext/uri/tests/rfc3986/modification/scheme_success_basic.phpt
new file mode 100644
index 000000000000..695cb9c7f1e4
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/scheme_success_basic.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - scheme - basic case
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("HTTP");
+
+var_dump($uri1->getRawScheme());
+var_dump($uri2->getRawScheme());
+var_dump($uri2->toRawString());
+var_dump($uri2->getScheme());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(5) "https"
+string(4) "HTTP"
+string(18) "HTTP://example.com"
+string(4) "http"
+string(18) "http://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/scheme_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/scheme_success_unset_existing.phpt
new file mode 100644
index 000000000000..67d447e8810e
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/scheme_success_unset_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - scheme - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withScheme(null);
+
+var_dump($uri1->getRawScheme());
+var_dump($uri2->getRawScheme());
+var_dump($uri2->toRawString());
+var_dump($uri2->getScheme());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(5) "https"
+NULL
+string(13) "//example.com"
+NULL
+string(13) "//example.com"
diff --git a/ext/uri/tests/rfc3986/modification/scheme_success_unset_non_existent.phpt b/ext/uri/tests/rfc3986/modification/scheme_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..4aa9b2d9dd2c
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/scheme_success_unset_non_existent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - scheme - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withScheme(null);
+
+var_dump($uri1->getRawScheme());
+var_dump($uri2->getRawScheme());
+var_dump($uri2->toRawString());
+var_dump($uri2->getScheme());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(8) "/foo/bar"
+NULL
+string(8) "/foo/bar"
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_error_reserved.phpt b/ext/uri/tests/rfc3986/modification/userinfo_error_reserved.phpt
new file mode 100644
index 000000000000..a5f820b0d59f
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_error_reserved.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - reserved characters
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo("us/r:password"); // us/r:password
+} catch (Uri\InvalidUriException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified userinfo is malformed
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_success_empty.phpt b/ext/uri/tests/rfc3986/modification/userinfo_success_empty.phpt
new file mode 100644
index 000000000000..ab753e6de507
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_success_empty.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo("");
+
+var_dump($uri1->getRawUserInfo());
+var_dump($uri2->getRawUserInfo());
+var_dump($uri2->getUserInfo());
+
+?>
+--EXPECT--
+NULL
+string(0) ""
+string(0) ""
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_success_encoded.phpt b/ext/uri/tests/rfc3986/modification/userinfo_success_encoded.phpt
new file mode 100644
index 000000000000..a4b7409d6706
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_success_encoded.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo("%75s%2Fr:password"); // us/r:password
+
+var_dump($uri1->getRawUserInfo());
+var_dump($uri2->getRawUserInfo());
+var_dump($uri2->toRawString());
+var_dump($uri2->getUserInfo());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(17) "%75s%2Fr:password"
+string(37) "https://%75s%2Fr:password@example.com"
+string(15) "us%2Fr:password"
+string(35) "https://us%2Fr:password@example.com"
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_success_existing.phpt b/ext/uri/tests/rfc3986/modification/userinfo_success_existing.phpt
new file mode 100644
index 000000000000..a13e091075cb
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_success_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo("user:password");
+
+var_dump($uri1->getRawUserInfo());
+var_dump($uri2->getRawUserInfo());
+var_dump($uri2->toRawString());
+var_dump($uri2->getUserInfo());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(5) ":pass"
+string(13) "user:password"
+string(33) "https://user:password@example.com"
+string(13) "user:password"
+string(33) "https://user:password@example.com"
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_success_new.phpt b/ext/uri/tests/rfc3986/modification/userinfo_success_new.phpt
new file mode 100644
index 000000000000..dc49c10d7526
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_success_new.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - adding a new one
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo("user:password");
+
+var_dump($uri1->getRawUserInfo());
+var_dump($uri2->getRawUserInfo());
+var_dump($uri2->toRawString());
+var_dump($uri2->getUserInfo());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+string(13) "user:password"
+string(33) "https://user:password@example.com"
+string(13) "user:password"
+string(33) "https://user:password@example.com"
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/userinfo_success_unset_existing.phpt
new file mode 100644
index 000000000000..0f4e7219cbd3
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_success_unset_existing.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo(null);
+
+var_dump($uri1->getRawUserInfo());
+var_dump($uri2->getRawUserInfo());
+var_dump($uri2->toRawString());
+var_dump($uri2->getUserInfo());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+string(13) "user:password"
+NULL
+string(19) "https://example.com"
+NULL
+string(19) "https://example.com"
diff --git a/ext/uri/tests/rfc3986/modification/userinfo_success_unset_non_existent.phpt b/ext/uri/tests/rfc3986/modification/userinfo_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..9aa2a5999ac4
--- /dev/null
+++ b/ext/uri/tests/rfc3986/modification/userinfo_success_unset_non_existent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\Rfc3986\Uri component modification - userinfo - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withUserInfo(null);
+
+var_dump($uri1->getRawUserInfo());
+var_dump($uri2->getRawUserInfo());
+var_dump($uri2->toRawString());
+var_dump($uri2->getUserInfo());
+var_dump($uri2->toString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(8) "/foo/bar"
+NULL
+string(8) "/foo/bar"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_auto_encode.phpt b/ext/uri/tests/whatwg/modification/fragment_success_auto_encode.phpt
new file mode 100644
index 000000000000..ac74b8668cd2
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_auto_encode.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - characters from the percent encode set
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("<>");
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(6) "%3C%3E"
+string(27) "https://example.com/#%3C%3E"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_empty.phpt b/ext/uri/tests/whatwg/modification/fragment_success_empty.phpt
new file mode 100644
index 000000000000..5b3aa9426232
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_empty.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("");
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_encoded.phpt b/ext/uri/tests/whatwg/modification/fragment_success_encoded.phpt
new file mode 100644
index 000000000000..9473ea9535f9
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("foo%3db%61r"); // foo=bar
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(11) "foo%3db%61r"
+string(32) "https://example.com/#foo%3db%61r"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_existing.phpt b/ext/uri/tests/whatwg/modification/fragment_success_existing.phpt
new file mode 100644
index 000000000000..0a7d253434d4
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("bar");
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(3) "foo"
+string(3) "bar"
+string(24) "https://example.com/#bar"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt b/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt
new file mode 100644
index 000000000000..12ce6d6e6b64
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - only a hashmark character
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("#");
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(21) "https://example.com/#"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_unicode.phpt b/ext/uri/tests/whatwg/modification/fragment_success_unicode.phpt
new file mode 100644
index 000000000000..c609b5df042d
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_unicode.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - unicode characters
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("ő");
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(6) "%C5%91"
+string(27) "https://example.com/#%C5%91"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_unset_existing.phpt b/ext/uri/tests/whatwg/modification/fragment_success_unset_existing.phpt
new file mode 100644
index 000000000000..585707e497e5
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_unset_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withFragment(null);
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(3) "foo"
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_unset_non_existent.phpt b/ext/uri/tests/whatwg/modification/fragment_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..21e295db23ec
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_unset_non_existent.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - unsetting not-existent
+--EXTENSIONS--
+uri
+--FILE--
+withFragment(null);
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/fragment_success_with_leading_hashmark.phpt b/ext/uri/tests/whatwg/modification/fragment_success_with_leading_hashmark.phpt
new file mode 100644
index 000000000000..4523388223b6
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/fragment_success_with_leading_hashmark.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - fragment - with leading hashmark
+--EXTENSIONS--
+uri
+--FILE--
+withFragment("#fragment");
+
+var_dump($url1->getFragment());
+var_dump($url2->getFragment());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(8) "fragment"
+string(29) "https://example.com/#fragment"
diff --git a/ext/uri/tests/whatwg/modification/host_error_empty.phpt b/ext/uri/tests/whatwg/modification/host_error_empty.phpt
new file mode 100644
index 000000000000..f9f473957860
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_empty.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withHost("");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed (HostMissing)
diff --git a/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque1.phpt b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque1.phpt
new file mode 100644
index 000000000000..8aebc5f7057d
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque1.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - forbidden host code point
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex@mple.com");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed (HostInvalidCodePoint)
diff --git a/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque2.phpt b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque2.phpt
new file mode 100644
index 000000000000..6d8b0c8b6565
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque2.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - forbidden host code point
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex#mple.com");
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(2) "ex"
diff --git a/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque3.phpt b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque3.phpt
new file mode 100644
index 000000000000..7d32dcba2c6c
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_opaque3.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - forbidden host codepoint
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex#mple.com");
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(2) "ex"
diff --git a/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special1.phpt b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special1.phpt
new file mode 100644
index 000000000000..a27c26381b4e
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special1.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - forbidden domain code point
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex@mple.com");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed (DomainInvalidCodePoint)
diff --git a/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special2.phpt b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special2.phpt
new file mode 100644
index 000000000000..a0626d99c51a
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special2.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - forbidden domain code point
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex:mple.com");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed
diff --git a/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special3.phpt b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special3.phpt
new file mode 100644
index 000000000000..a0626d99c51a
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_forbidden_host_codepoint_special3.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - forbidden domain code point
+--EXTENSIONS--
+uri
+--FILE--
+withHost("ex:mple.com");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed
diff --git a/ext/uri/tests/whatwg/modification/host_error_unset_existing.phpt b/ext/uri/tests/whatwg/modification/host_error_unset_existing.phpt
new file mode 100644
index 000000000000..ca5d5a0201d7
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_error_unset_existing.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withHost(null);
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified host is malformed (HostMissing)
diff --git a/ext/uri/tests/whatwg/modification/host_success_encoded.phpt b/ext/uri/tests/whatwg/modification/host_success_encoded.phpt
new file mode 100644
index 000000000000..9604476554fc
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_success_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withHost("%65xample.net"); // example.net
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(11) "example.net"
+string(20) "https://example.net/"
diff --git a/ext/uri/tests/whatwg/modification/host_success_existing.phpt b/ext/uri/tests/whatwg/modification/host_success_existing.phpt
new file mode 100644
index 000000000000..57394c851c77
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withHost("example.net");
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(11) "example.net"
+string(20) "https://example.net/"
diff --git a/ext/uri/tests/whatwg/modification/host_success_idna.phpt b/ext/uri/tests/whatwg/modification/host_success_idna.phpt
new file mode 100644
index 000000000000..919e22934dfa
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_success_idna.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - IDNA characters
+--EXTENSIONS--
+uri
+--FILE--
+withHost("éxämple.com");
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+var_dump($url2->toAsciiString());
+var_dump($url2->getUnicodeHost());
+var_dump($url2->toUnicodeString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(19) "xn--xmple-gra7a.com"
+string(28) "https://xn--xmple-gra7a.com/"
+string(13) "éxämple.com"
+string(22) "https://éxämple.com/"
diff --git a/ext/uri/tests/whatwg/modification/host_success_ipv4.phpt b/ext/uri/tests/whatwg/modification/host_success_ipv4.phpt
new file mode 100644
index 000000000000..fef9e0fc6376
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_success_ipv4.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - IPv4 address
+--EXTENSIONS--
+uri
+--FILE--
+withHost("192.168.0.1");
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(11) "192.168.0.1"
+string(20) "https://192.168.0.1/"
diff --git a/ext/uri/tests/whatwg/modification/host_success_ipv6.phpt b/ext/uri/tests/whatwg/modification/host_success_ipv6.phpt
new file mode 100644
index 000000000000..2124b70ef56d
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/host_success_ipv6.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - IPv6 address
+--EXTENSIONS--
+uri
+--FILE--
+withHost("[2001:0db8:3333:4444:5555:6666:7777:8888]");
+
+var_dump($url1->getAsciiHost());
+var_dump($url2->getAsciiHost());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(11) "example.com"
+string(40) "[2001:db8:3333:4444:5555:6666:7777:8888]"
+string(49) "https://[2001:db8:3333:4444:5555:6666:7777:8888]/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_auto_encoded.phpt b/ext/uri/tests/whatwg/modification/password_success_auto_encoded.phpt
new file mode 100644
index 000000000000..5db68cb90a62
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_auto_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - password - characters from the percent encode set
+--EXTENSIONS--
+uri
+--FILE--
+withPassword("p:s/");
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(8) "password"
+string(8) "p%3As%2F"
+string(34) "https://user:p%3As%2F@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_empty.phpt b/ext/uri/tests/whatwg/modification/password_success_empty.phpt
new file mode 100644
index 000000000000..40c350cc1b30
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_empty.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - password - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withPassword("");
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_encoded.phpt b/ext/uri/tests/whatwg/modification/password_success_encoded.phpt
new file mode 100644
index 000000000000..82b172d914af
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - password - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withPassword("p%61ssword");
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(10) "p%61ssword"
+string(32) "https://:p%61ssword@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_existing.phpt b/ext/uri/tests/whatwg/modification/password_success_existing.phpt
new file mode 100644
index 000000000000..61b0ce3909b4
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - password - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withPassword("password");
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(4) "pass"
+string(8) "password"
+string(30) "https://:password@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_new.phpt b/ext/uri/tests/whatwg/modification/password_success_new.phpt
new file mode 100644
index 000000000000..8357b9972109
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_new.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - adding a new one
+--EXTENSIONS--
+uri
+--FILE--
+withPassword("password");
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(8) "password"
+string(30) "https://:password@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_unset_existing.phpt b/ext/uri/tests/whatwg/modification/password_success_unset_existing.phpt
new file mode 100644
index 000000000000..957973b062a2
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_unset_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - password - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withPassword(null);
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(8) "password"
+NULL
+string(29) "https://username@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_unset_non_existent1.phpt b/ext/uri/tests/whatwg/modification/password_success_unset_non_existent1.phpt
new file mode 100644
index 000000000000..357b33b78c0b
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_unset_non_existent1.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - password - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withPassword(null);
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/password_success_unset_non_existent2.phpt b/ext/uri/tests/whatwg/modification/password_success_unset_non_existent2.phpt
new file mode 100644
index 000000000000..a2140669ee8a
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/password_success_unset_non_existent2.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withPassword(null);
+
+var_dump($url1->getPassword());
+var_dump($url2->getPassword());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(29) "https://username@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt b/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
new file mode 100644
index 000000000000..59f539124c68
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - path - characters from the percent encode set
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/p^th#");
+
+var_dump($url1->getPath());
+var_dump($url2->getPath());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(1) "/"
+string(8) "/p^th%23"
+string(27) "https://example.com/p^th%23"
diff --git a/ext/uri/tests/whatwg/modification/path_success_empty.phpt b/ext/uri/tests/whatwg/modification/path_success_empty.phpt
new file mode 100644
index 000000000000..9e8d3495445a
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/path_success_empty.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - path - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withPath("");
+
+var_dump($url1->getPath());
+var_dump($url2->getPath());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(1) "/"
+string(1) "/"
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/path_success_encoded.phpt b/ext/uri/tests/whatwg/modification/path_success_encoded.phpt
new file mode 100644
index 000000000000..b698c4691b0c
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/path_success_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - path - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/foo%2Fb%61r"); // /foo/bar
+
+var_dump($url1->getPath());
+var_dump($url2->getPath());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(1) "/"
+string(12) "/foo%2Fb%61r"
+string(31) "https://example.com/foo%2Fb%61r"
diff --git a/ext/uri/tests/whatwg/modification/path_success_existing.phpt b/ext/uri/tests/whatwg/modification/path_success_existing.phpt
new file mode 100644
index 000000000000..113c8c88d48b
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/path_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - path - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/baz");
+
+var_dump($url1->getPath());
+var_dump($url2->getPath());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(8) "/foo/bar"
+string(4) "/baz"
+string(23) "https://example.com/baz"
diff --git a/ext/uri/tests/whatwg/modification/path_success_unicode.phpt b/ext/uri/tests/whatwg/modification/path_success_unicode.phpt
new file mode 100644
index 000000000000..d195a6dadbc1
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/path_success_unicode.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - path - unicode characters
+--EXTENSIONS--
+uri
+--FILE--
+withPath("/ő");
+
+var_dump($url1->getPath());
+var_dump($url2->getPath());
+var_dump($url2->toAsciiString());
+?>
+--EXPECT--
+string(1) "/"
+string(7) "/%C5%91"
+string(26) "https://example.com/%C5%91"
diff --git a/ext/uri/tests/whatwg/modification/path_success_without_leading_slash.phpt b/ext/uri/tests/whatwg/modification/path_success_without_leading_slash.phpt
new file mode 100644
index 000000000000..1149287dc531
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/path_success_without_leading_slash.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - path - without leading slash
+--EXTENSIONS--
+uri
+--FILE--
+withPath("foo/bar");
+
+var_dump($url1->getPath());
+var_dump($url2->getPath());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(1) "/"
+string(8) "/foo/bar"
+string(27) "https://example.com/foo/bar"
diff --git a/ext/uri/tests/whatwg/modification/port_error_negative.phpt b/ext/uri/tests/whatwg/modification/port_error_negative.phpt
new file mode 100644
index 000000000000..5de87d7e7608
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_error_negative.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - negative number
+--EXTENSIONS--
+uri
+--FILE--
+withPort(-1);
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified port is malformed
diff --git a/ext/uri/tests/whatwg/modification/port_error_too_large.phpt b/ext/uri/tests/whatwg/modification/port_error_too_large.phpt
new file mode 100644
index 000000000000..4060056b0a56
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_error_too_large.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - host - larger than a 16-bit unsigned integer
+--EXTENSIONS--
+uri
+--FILE--
+withPort(65536);
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified port is malformed (PortOutOfRange)
diff --git a/ext/uri/tests/whatwg/modification/port_success_existing.phpt b/ext/uri/tests/whatwg/modification/port_success_existing.phpt
new file mode 100644
index 000000000000..e5ea806bd1a4
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withPort(443);
+
+var_dump($url1->getPort());
+var_dump($url2->getPort());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+int(80)
+int(443)
+string(24) "scheme://example.com:443"
diff --git a/ext/uri/tests/whatwg/modification/port_success_new.phpt b/ext/uri/tests/whatwg/modification/port_success_new.phpt
new file mode 100644
index 000000000000..85098b21b7b3
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_success_new.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - adding a new one
+--EXTENSIONS--
+uri
+--FILE--
+withPort(443);
+
+var_dump($url1->getPort());
+var_dump($url2->getPort());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+int(443)
+string(24) "scheme://example.com:443"
diff --git a/ext/uri/tests/whatwg/modification/port_success_special1.phpt b/ext/uri/tests/whatwg/modification/port_success_special1.phpt
new file mode 100644
index 000000000000..ba85449e4353
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_success_special1.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - adding a new one for a special URL
+--EXTENSIONS--
+uri
+--FILE--
+withPort(80);
+
+var_dump($url1->getPort());
+var_dump($url2->getPort());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+int(88)
+NULL
+string(19) "http://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/port_success_special2.phpt b/ext/uri/tests/whatwg/modification/port_success_special2.phpt
new file mode 100644
index 000000000000..d9f0074f0532
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_success_special2.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - adding a new one for a special URL
+--EXTENSIONS--
+uri
+--FILE--
+withPort(443);
+
+var_dump($url1->getPort());
+var_dump($url2->getPort());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/port_success_unset_existing.phpt b/ext/uri/tests/whatwg/modification/port_success_unset_existing.phpt
new file mode 100644
index 000000000000..c280c181b19a
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_success_unset_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withPort(null);
+
+var_dump($url1->getPort());
+var_dump($url2->getPort());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+int(80)
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/port_success_unset_non_existent.phpt b/ext/uri/tests/whatwg/modification/port_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..d364c72d7002
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/port_success_unset_non_existent.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - port - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withPort(null);
+
+var_dump($url2->getPort());
+var_dump($url2->getPort());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(18) "ftp://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/query_success_auto_encoded.phpt b/ext/uri/tests/whatwg/modification/query_success_auto_encoded.phpt
new file mode 100644
index 000000000000..fc487f33797d
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_auto_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - characters from the percent encode set
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("#foo ");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(9) "%23foo%20"
+string(30) "https://example.com/?%23foo%20"
diff --git a/ext/uri/tests/whatwg/modification/query_success_context_sensitive_reserved.phpt b/ext/uri/tests/whatwg/modification/query_success_context_sensitive_reserved.phpt
new file mode 100644
index 000000000000..cb220a0a33ef
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_context_sensitive_reserved.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - context-sensitive reserved character
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("?foo=bar");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(7) "foo=bar"
+string(28) "https://example.com/?foo=bar"
diff --git a/ext/uri/tests/whatwg/modification/query_success_empty.phpt b/ext/uri/tests/whatwg/modification/query_success_empty.phpt
new file mode 100644
index 000000000000..dcb98bc37689
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_empty.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/query_success_encoded.phpt b/ext/uri/tests/whatwg/modification/query_success_encoded.phpt
new file mode 100644
index 000000000000..f9935015f7bd
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("foo%3db%61r"); // foo=bar
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(11) "foo%3db%61r"
+string(32) "https://example.com/?foo%3db%61r"
diff --git a/ext/uri/tests/whatwg/modification/query_success_existing.phpt b/ext/uri/tests/whatwg/modification/query_success_existing.phpt
new file mode 100644
index 000000000000..b3429b58b865
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - changing an existing one
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("foo=bar&baz=qux");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(7) "foo=bar"
+string(15) "foo=bar&baz=qux"
+string(36) "https://example.com/?foo=bar&baz=qux"
diff --git a/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt b/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt
new file mode 100644
index 000000000000..18a1593a9819
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - only a question mark character
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("?");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(21) "https://example.com/?"
diff --git a/ext/uri/tests/whatwg/modification/query_success_unicode.phpt b/ext/uri/tests/whatwg/modification/query_success_unicode.phpt
new file mode 100644
index 000000000000..526138f8954a
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_unicode.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - unicode characters
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("ő");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(6) "%C5%91"
+string(27) "https://example.com/?%C5%91"
diff --git a/ext/uri/tests/whatwg/modification/query_success_unset_existing.phpt b/ext/uri/tests/whatwg/modification/query_success_unset_existing.phpt
new file mode 100644
index 000000000000..cb5a2a701e24
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_unset_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withQuery(null);
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(7) "foo=bar"
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/query_success_unset_non_existent.phpt b/ext/uri/tests/whatwg/modification/query_success_unset_non_existent.phpt
new file mode 100644
index 000000000000..6456dcacac7f
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_unset_non_existent.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - unsetting not-existent
+--EXTENSIONS--
+uri
+--FILE--
+withQuery(null);
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/query_success_with_leading_question_mark.phpt b/ext/uri/tests/whatwg/modification/query_success_with_leading_question_mark.phpt
new file mode 100644
index 000000000000..ce67dcb6bb35
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/query_success_with_leading_question_mark.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - query - with leading question mark
+--EXTENSIONS--
+uri
+--FILE--
+withQuery("?foo=bar");
+
+var_dump($url1->getQuery());
+var_dump($url2->getQuery());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(7) "foo=bar"
+string(35) "https://example.com/foo/bar?foo=bar"
diff --git a/ext/uri/tests/whatwg/modification/scheme_error_empty.phpt b/ext/uri/tests/whatwg/modification/scheme_error_empty.phpt
new file mode 100644
index 000000000000..0460fa72945f
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/scheme_error_empty.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - scheme - empty string
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified scheme is malformed
diff --git a/ext/uri/tests/whatwg/modification/scheme_error_encoded.phpt b/ext/uri/tests/whatwg/modification/scheme_error_encoded.phpt
new file mode 100644
index 000000000000..a52dc65dadd8
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/scheme_error_encoded.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - scheme - URL encoded characters
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("http%73");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified scheme is malformed
diff --git a/ext/uri/tests/whatwg/modification/scheme_error_invalid.phpt b/ext/uri/tests/whatwg/modification/scheme_error_invalid.phpt
new file mode 100644
index 000000000000..21cd04346a3c
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/scheme_error_invalid.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - scheme - invalid characters
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("http?");
+} catch (Uri\WhatWg\InvalidUrlException $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+The specified scheme is malformed
diff --git a/ext/uri/tests/whatwg/modification/scheme_success_basic.phpt b/ext/uri/tests/whatwg/modification/scheme_success_basic.phpt
new file mode 100644
index 000000000000..6f66b30b2b7b
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/scheme_success_basic.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - scheme - basic case
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("HTTP");
+
+var_dump($url1->getScheme());
+var_dump($url2->getScheme());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(5) "https"
+string(4) "http"
+string(19) "http://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/scheme_success_colon.phpt b/ext/uri/tests/whatwg/modification/scheme_success_colon.phpt
new file mode 100644
index 000000000000..052eca6e247b
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/scheme_success_colon.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - scheme - trailing colon
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("http:");
+
+var_dump($url1->getScheme());
+var_dump($url2->getScheme());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(5) "https"
+string(4) "http"
+string(19) "http://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/scheme_success_full.phpt b/ext/uri/tests/whatwg/modification/scheme_success_full.phpt
new file mode 100644
index 000000000000..957a1fab7aff
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/scheme_success_full.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - scheme - trailing colon and double slash
+--EXTENSIONS--
+uri
+--FILE--
+withScheme("http://");
+
+var_dump($url1->getScheme());
+var_dump($url2->getScheme());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(5) "https"
+string(4) "http"
+string(19) "http://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/username_success_auto_encoded.phpt b/ext/uri/tests/whatwg/modification/username_success_auto_encoded.phpt
new file mode 100644
index 000000000000..bc0beab49649
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/username_success_auto_encoded.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - characters from the percent encode set
+--EXTENSIONS--
+uri
+--FILE--
+withUsername("u:s/r");
+
+var_dump($url1->getUsername());
+var_dump($url2->getUsername());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(4) "user"
+string(9) "u%3As%2Fr"
+string(39) "https://u%3As%2Fr:password@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/username_success_existing.phpt b/ext/uri/tests/whatwg/modification/username_success_existing.phpt
new file mode 100644
index 000000000000..2506acf85d71
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/username_success_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - changing existing
+--EXTENSIONS--
+uri
+--FILE--
+withUsername("username");
+
+var_dump($url1->getUsername());
+var_dump($url2->getUsername());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(4) "user"
+string(8) "username"
+string(29) "https://username@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/username_success_new.phpt b/ext/uri/tests/whatwg/modification/username_success_new.phpt
new file mode 100644
index 000000000000..56666a68ea7d
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/username_success_new.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - adding a new one
+--EXTENSIONS--
+uri
+--FILE--
+withUsername("username");
+
+var_dump($url1->getUsername());
+var_dump($url2->getUsername());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+string(8) "username"
+string(29) "https://username@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/username_success_unset_existing.phpt b/ext/uri/tests/whatwg/modification/username_success_unset_existing.phpt
new file mode 100644
index 000000000000..f71deff3f207
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/username_success_unset_existing.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - unsetting existing
+--EXTENSIONS--
+uri
+--FILE--
+withUsername(null);
+
+var_dump($url1->getUsername());
+var_dump($url2->getUsername());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+string(8) "username"
+NULL
+string(30) "https://:password@example.com/"
diff --git a/ext/uri/tests/whatwg/modification/username_success_unset_non_existent1.phpt b/ext/uri/tests/whatwg/modification/username_success_unset_non_existent1.phpt
new file mode 100644
index 000000000000..44a648610424
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/username_success_unset_non_existent1.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withUsername(null);
+
+var_dump($url1->getUsername());
+var_dump($url2->getUsername());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/modification/username_success_unset_non_existent2.phpt b/ext/uri/tests/whatwg/modification/username_success_unset_non_existent2.phpt
new file mode 100644
index 000000000000..e5af4efb223d
--- /dev/null
+++ b/ext/uri/tests/whatwg/modification/username_success_unset_non_existent2.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test Uri\WhatWg\Url component modification - username - unsetting non-existent
+--EXTENSIONS--
+uri
+--FILE--
+withUsername(null);
+
+var_dump($url1->getUsername());
+var_dump($url2->getUsername());
+var_dump($url2->toAsciiString());
+
+?>
+--EXPECT--
+NULL
+NULL
+string(30) "https://:password@example.com/"
From 3a9d59971b68906fcf54dfc4d96f0acaa04f1db9 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sat, 25 Oct 2025 21:01:28 +0200
Subject: [PATCH 07/10] phar: Use a loop instead of goto when looking for
extensions (#20289)
This gets rid of a TODO and makes the code clearer.
---
ext/phar/phar.c | 57 ++++++++++++++++++++++---------------------------
1 file changed, 26 insertions(+), 31 deletions(-)
diff --git a/ext/phar/phar.c b/ext/phar/phar.c
index 15cdddb21af4..b7bd61b86019 100644
--- a/ext/phar/phar.c
+++ b/ext/phar/phar.c
@@ -2034,45 +2034,40 @@ zend_result phar_detect_phar_fname_ext(const char *filename, size_t filename_len
}
}
- // TODO Use some sort of loop here instead of a goto
pos = memchr(filename + 1, '.', filename_len);
-next_extension:
- if (!pos) {
- return FAILURE;
- }
-
- while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
- pos = memchr(pos + 1, '.', filename_len - (pos - filename) - 1);
- if (!pos) {
- return FAILURE;
+ while (pos) {
+ while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
+ pos = memchr(pos + 1, '.', filename_len - (pos - filename) - 1);
+ if (!pos) {
+ return FAILURE;
+ }
}
- }
- slash = memchr(pos, '/', filename_len - (pos - filename));
+ slash = memchr(pos, '/', filename_len - (pos - filename));
- if (!slash) {
- /* this is a url like "phar://blah.phar" with no directory */
- *ext_str = pos;
- *ext_len = strlen(pos);
+ if (!slash) {
+ /* this is a url like "phar://blah.phar" with no directory */
+ *ext_str = pos;
+ *ext_len = strlen(pos);
- /* file extension must contain "phar" */
- return phar_check_str(filename, *ext_str, *ext_len, executable, for_create);
- }
+ /* file extension must contain "phar" */
+ return phar_check_str(filename, *ext_str, *ext_len, executable, for_create);
+ }
- /* we've found an extension that ends at a directory separator */
- *ext_str = pos;
- *ext_len = slash - pos;
+ /* we've found an extension that ends at a directory separator */
+ *ext_str = pos;
+ *ext_len = slash - pos;
- if (phar_check_str(filename, *ext_str, *ext_len, executable, for_create) == SUCCESS) {
- return SUCCESS;
- }
+ if (phar_check_str(filename, *ext_str, *ext_len, executable, for_create) == SUCCESS) {
+ return SUCCESS;
+ }
- /* look for more extensions */
- pos = strchr(pos + 1, '.');
- if (pos) {
- *ext_str = NULL;
- *ext_len = 0;
- goto next_extension;
+ /* look for more extensions */
+ pos = strchr(pos + 1, '.');
+ if (pos) {
+ *ext_str = NULL;
+ *ext_len = 0;
+ }
}
return FAILURE;
From 86a15f9a1bf7d9d5f2774f78157ae5fc48cd4e77 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sat, 25 Oct 2025 21:01:41 +0200
Subject: [PATCH 08/10] phar: Remove redundant *error check (#20288)
The failure of the iterator is already checked above at line 1157.
---
ext/phar/tar.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/ext/phar/tar.c b/ext/phar/tar.c
index 8a5df4f443dc..4847597cce2e 100644
--- a/ext/phar/tar.c
+++ b/ext/phar/tar.c
@@ -1242,12 +1242,6 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) void phar_tar_flush(phar_archive_data *phar, z
php_stream_close(oldfile);
}
- /* on error in the hash iterator above, error is set */
- if (*error) {
- php_stream_close(newfile);
- return;
- }
-
if (phar->fp && pass.free_fp) {
php_stream_close(phar->fp);
}
From 3884438fe33b0a45fe0839d4a8b352b28b00b800 Mon Sep 17 00:00:00 2001
From: David Carlier
Date: Sat, 25 Oct 2025 21:00:10 +0100
Subject: [PATCH 09/10] ext/zip: ZipArchive callback missing
zend_release_fcall_info_cache
during FCC conversion (79b9fe3) in the rare cases where the lib
fails to allocate the memory for these callbacks, we return false
directly.
close GH-20293
---
NEWS | 5 +++++
ext/zip/php_zip.c | 2 ++
2 files changed, 7 insertions(+)
diff --git a/NEWS b/NEWS
index 23933494c825..d8d7cda775b5 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,11 @@ PHP NEWS
. Fixed bug GH-19798: XP_SOCKET XP_SSL (Socket stream modules): Incorrect
condition for Win32/Win64. (Jakub Zelenka)
+- Zip:
+ . Fixed missing zend_release_fcall_info_cache on the following methods
+ ZipArchive::registerProgressCallback() and ZipArchive::registerCancelCallback()
+ on failure. (David Carlier)
+
23 Oct 2025, PHP 8.5.0RC3
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index 1a49bc1d10bd..34ac611c99b7 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -3026,6 +3026,7 @@ PHP_METHOD(ZipArchive, registerProgressCallback)
/* register */
if (zip_register_progress_callback_with_state(intern, rate, php_zip_progress_callback, php_zip_progress_callback_free, obj)) {
+ zend_release_fcall_info_cache(&fcc);
RETURN_FALSE;
}
zend_fcc_dup(&obj->progress_callback, &fcc);
@@ -3081,6 +3082,7 @@ PHP_METHOD(ZipArchive, registerCancelCallback)
/* register */
if (zip_register_cancel_callback_with_state(intern, php_zip_cancel_callback, php_zip_cancel_callback_free, obj)) {
+ zend_release_fcall_info_cache(&fcc);
RETURN_FALSE;
}
zend_fcc_dup(&obj->cancel_callback, &fcc);
From 275ec6f3352ffa423409751f4488439179c65528 Mon Sep 17 00:00:00 2001
From: Gina Peter Banyard
Date: Sat, 25 Oct 2025 22:36:09 +0100
Subject: [PATCH 10/10] Zend: make zend_copy_parameters_array() private
(#20265)
And slightly refactor implementation.
---
UPGRADING.INTERNALS | 1 +
Zend/zend_API.c | 22 ----------------------
Zend/zend_API.h | 3 ---
Zend/zend_closures.c | 17 +++++++++++++++--
4 files changed, 16 insertions(+), 27 deletions(-)
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 42e52480bbb9..15f6620496d4 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -34,6 +34,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES
printf family.
. The zval_dtor() alias of zval_ptr_dtor_nogc() has been removed.
Call zval_ptr_dtor_nogc() directly instead.
+ . The internal zend_copy_parameters_array() function is no longer exposed.
========================
2. Build system changes
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 1b97974686ed..e05cc7e506f6 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -76,28 +76,6 @@ ZEND_API zend_result zend_get_parameters_array_ex(uint32_t param_count, zval *ar
}
/* }}} */
-ZEND_API zend_result zend_copy_parameters_array(uint32_t param_count, zval *argument_array) /* {{{ */
-{
- zval *param_ptr;
- uint32_t arg_count;
-
- param_ptr = ZEND_CALL_ARG(EG(current_execute_data), 1);
- arg_count = ZEND_CALL_NUM_ARGS(EG(current_execute_data));
-
- if (param_count>arg_count) {
- return FAILURE;
- }
-
- while (param_count-->0) {
- Z_TRY_ADDREF_P(param_ptr);
- zend_hash_next_index_insert_new(Z_ARRVAL_P(argument_array), param_ptr);
- param_ptr++;
- }
-
- return SUCCESS;
-}
-/* }}} */
-
ZEND_API ZEND_COLD void zend_wrong_param_count(void) /* {{{ */
{
const char *space;
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 4e2954c0a65c..24e686b721ef 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -347,9 +347,6 @@ ZEND_API void zend_set_dl_use_deepbind(bool use_deepbind);
ZEND_API zend_result zend_get_parameters_array_ex(uint32_t param_count, zval *argument_array);
-/* internal function to efficiently copy parameters when executing __call() */
-ZEND_API zend_result zend_copy_parameters_array(uint32_t param_count, zval *argument_array);
-
#define zend_get_parameters_array(ht, param_count, argument_array) \
zend_get_parameters_array_ex(param_count, argument_array)
#define zend_parse_parameters_none() \
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 4c2b85f5d48c..948139a86590 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -287,6 +287,19 @@ ZEND_METHOD(Closure, bindTo)
do_closure_bind(return_value, ZEND_THIS, newthis, scope_obj, scope_str);
}
+static void zend_copy_parameters_array(const uint32_t param_count, HashTable *argument_array) /* {{{ */
+{
+ zval *param_ptr = ZEND_CALL_ARG(EG(current_execute_data), 1);
+
+ ZEND_ASSERT(param_count <= ZEND_CALL_NUM_ARGS(EG(current_execute_data)));
+
+ for (uint32_t i = 0; i < param_count; i++) {
+ Z_TRY_ADDREF_P(param_ptr);
+ zend_hash_next_index_insert_new(argument_array, param_ptr);
+ param_ptr++;
+ }
+}
+
static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@@ -310,14 +323,14 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
array_init_size(&fci.params[1], ZEND_NUM_ARGS() + zend_hash_num_elements(EX(extra_named_params)));
/* Avoid conversion from packed to mixed later. */
zend_hash_real_init_mixed(Z_ARRVAL(fci.params[1]));
- zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
+ zend_copy_parameters_array(ZEND_NUM_ARGS(), Z_ARRVAL(fci.params[1]));
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, named_param_zval) {
Z_TRY_ADDREF_P(named_param_zval);
zend_hash_add_new(Z_ARRVAL(fci.params[1]), name, named_param_zval);
} ZEND_HASH_FOREACH_END();
} else if (ZEND_NUM_ARGS()) {
array_init_size(&fci.params[1], ZEND_NUM_ARGS());
- zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
+ zend_copy_parameters_array(ZEND_NUM_ARGS(), Z_ARRVAL(fci.params[1]));
} else {
ZVAL_EMPTY_ARRAY(&fci.params[1]);
}