Commit cf3b30581d4 for php.net
commit cf3b30581d4cb93fd1b1d82bfad372d99caf8388
Author: Máté Kocsis <kocsismate@woohoolabs.com>
Date: Mon Nov 3 21:51:56 2025 +0100
Reorganize ext/uri tests - parsing (#20340)
diff --git a/ext/uri/tests/rfc3986/parsing/basic_error_multibyte.phpt b/ext/uri/tests/rfc3986/parsing/basic_error_multibyte.phpt
new file mode 100644
index 00000000000..cb6007c844a
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - multibyte character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("🐘");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/basic_error_null_byte1.phpt b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte1.phpt
new file mode 100644
index 00000000000..5695b8487cd
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte1.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - URI contains null byte
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://exam\0ple.com");
+} catch (ValueError $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+ValueError: Uri\Rfc3986\Uri::__construct(): Argument #1 ($uri) must not contain any null bytes
diff --git a/ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt
new file mode 100644
index 00000000000..85c779cc5af
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - URI contains null byte
+--FILE--
+<?php
+
+try {
+ Uri\Rfc3986\Uri::parse("https://exam\0ple.com");
+} catch (ValueError $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+ValueError: Uri\Rfc3986\Uri::parse(): Argument #1 ($uri) must not contain any null bytes
diff --git a/ext/uri/tests/rfc3986/parsing/basic_success_all.phpt b/ext/uri/tests/rfc3986/parsing/basic_success_all.phpt
new file mode 100644
index 00000000000..06d5edeb1c5
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_success_all.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - all components
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://user:info@example.com:443/foo/bar?abc=123&def=ghi#hashmark");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(4) "info"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ int(443)
+ ["path"]=>
+ string(8) "/foo/bar"
+ ["query"]=>
+ string(15) "abc=123&def=ghi"
+ ["fragment"]=>
+ string(8) "hashmark"
+}
+string(66) "https://user:info@example.com:443/foo/bar?abc=123&def=ghi#hashmark"
+string(66) "https://user:info@example.com:443/foo/bar?abc=123&def=ghi#hashmark"
diff --git a/ext/uri/tests/rfc3986/parsing/basic_sucess_email.phpt b/ext/uri/tests/rfc3986/parsing/basic_sucess_email.phpt
new file mode 100644
index 00000000000..9d0f01c9ac0
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_sucess_email.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - mailto email
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("mailto:user@example.com");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(6) "mailto"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(16) "user@example.com"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(23) "mailto:user@example.com"
+string(23) "mailto:user@example.com"
diff --git a/ext/uri/tests/rfc3986/parsing/basic_sucess_empty.phpt b/ext/uri/tests/rfc3986/parsing/basic_sucess_empty.phpt
new file mode 100644
index 00000000000..db72394c082
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_sucess_empty.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - empty string
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ NULL
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(0) ""
+string(0) ""
diff --git a/ext/uri/tests/rfc3986/parsing/basic_sucess_file.phpt b/ext/uri/tests/rfc3986/parsing/basic_sucess_file.phpt
new file mode 100644
index 00000000000..420b1071dd6
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_sucess_file.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - file
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("file:///E:/Documents%20and%20Settings");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(4) "file"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(0) ""
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(30) "/E:/Documents%20and%20Settings"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(37) "file:///E:/Documents%20and%20Settings"
+string(37) "file:///E:/Documents%20and%20Settings"
diff --git a/ext/uri/tests/rfc3986/parsing/basic_sucess_urn.phpt b/ext/uri/tests/rfc3986/parsing/basic_sucess_urn.phpt
new file mode 100644
index 00000000000..81fe978c9fc
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/basic_sucess_urn.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - basic - URN
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(3) "urn"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(41) "uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(45) "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66"
+string(45) "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66"
diff --git a/ext/uri/tests/rfc3986/parsing/host_error_multibyte.phpt b/ext/uri/tests/rfc3986/parsing/host_error_multibyte.phpt
new file mode 100644
index 00000000000..d901d0fdecb
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - multibyte character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://exḁmple.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/host_error_reserved.phpt b/ext/uri/tests/rfc3986/parsing/host_error_reserved.phpt
new file mode 100644
index 00000000000..0568a027f5b
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_error_reserved.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - reserved character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://ex[a]mple.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_empty1.phpt b/ext/uri/tests/rfc3986/parsing/host_success_empty1.phpt
new file mode 100644
index 00000000000..da1ea6f00af
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_empty1.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - empty
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(0) ""
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(8) "https://"
+string(8) "https://"
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_empty2.phpt b/ext/uri/tests/rfc3986/parsing/host_success_empty2.phpt
new file mode 100644
index 00000000000..9f392631a44
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_empty2.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - empty
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://user:pass@");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(0) ""
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(18) "https://user:pass@"
+string(18) "https://user:pass@"
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_empty3.phpt b/ext/uri/tests/rfc3986/parsing/host_success_empty3.phpt
new file mode 100644
index 00000000000..eb399678eaa
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_empty3.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - empty
+--FILE--
+<?php
+
+$uri = new Uri\Rfc3986\Uri("//");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ NULL
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(0) ""
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(2) "//"
+string(2) "//"
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_empty4.phpt b/ext/uri/tests/rfc3986/parsing/host_success_empty4.phpt
new file mode 100644
index 00000000000..ed470e00575
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_empty4.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - empty
+--FILE--
+<?php
+
+$uri = new Uri\Rfc3986\Uri("///");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ NULL
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(0) ""
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(3) "///"
+string(3) "///"
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_ipv4.phpt b/ext/uri/tests/rfc3986/parsing/host_success_ipv4.phpt
new file mode 100644
index 00000000000..20686431707
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_ipv4.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - IPv4
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://192.168.0.1");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "192.168.0.1"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(19) "https://192.168.0.1"
+string(19) "https://192.168.0.1"
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_ipv4_wrong.phpt b/ext/uri/tests/rfc3986/parsing/host_success_ipv4_wrong.phpt
new file mode 100644
index 00000000000..84664187c55
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_ipv4_wrong.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - wrong IPv4 format
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://192.168");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(7) "192.168"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(15) "https://192.168"
+string(15) "https://192.168"
diff --git a/ext/uri/tests/rfc3986/parsing/host_success_ipv6.phpt b/ext/uri/tests/rfc3986/parsing/host_success_ipv6.phpt
new file mode 100644
index 00000000000..2292fb15736
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_ipv6.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - IPv6
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://[2001:0db8:3333:4444:5555:6666:7777:8888]");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(41) "[2001:0db8:3333:4444:5555:6666:7777:8888]"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(49) "https://[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/parsing/host_success_ipvfuture.phpt b/ext/uri/tests/rfc3986/parsing/host_success_ipvfuture.phpt
new file mode 100644
index 00000000000..7b641372489
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/host_success_ipvfuture.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - host - IPvFuture
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://[v7.host]");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(9) "[v7.host]"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(17) "https://[v7.host]"
+string(17) "https://[v7.host]"
diff --git a/ext/uri/tests/rfc3986/parsing/path_error_multibyte.phpt b/ext/uri/tests/rfc3986/parsing/path_error_multibyte.phpt
new file mode 100644
index 00000000000..ded528d0308
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/path_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - path - multibyte character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://example.com/fȎȎ");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/path_error_reserved.phpt b/ext/uri/tests/rfc3986/parsing/path_error_reserved.phpt
new file mode 100644
index 00000000000..2a7efd70256
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/path_error_reserved.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - path - reserved character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://example.com/fo[o/ba]r/");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/path_success_relative_reference.phpt b/ext/uri/tests/rfc3986/parsing/path_success_relative_reference.phpt
new file mode 100644
index 00000000000..13a9169da3b
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/path_success_relative_reference.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - path - relative reference
+--FILE--
+<?php
+
+$uri = new Uri\Rfc3986\Uri("foo");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ NULL
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(3) "foo"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(3) "foo"
+string(3) "foo"
diff --git a/ext/uri/tests/rfc3986/parsing/path_success_relative_reference_absolute.phpt b/ext/uri/tests/rfc3986/parsing/path_success_relative_reference_absolute.phpt
new file mode 100644
index 00000000000..4096e00dc32
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/path_success_relative_reference_absolute.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - path - relative reference
+--FILE--
+<?php
+
+$uri = new Uri\Rfc3986\Uri("/foo");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ NULL
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(4) "/foo"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(4) "/foo"
+string(4) "/foo"
diff --git a/ext/uri/tests/rfc3986/parsing/path_success_relative_reference_only_query_fragment.phpt b/ext/uri/tests/rfc3986/parsing/path_success_relative_reference_only_query_fragment.phpt
new file mode 100644
index 00000000000..5e7ec7b68f7
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/path_success_relative_reference_only_query_fragment.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - path - relative reference
+--FILE--
+<?php
+
+$uri = new Uri\Rfc3986\Uri("?query#fragment");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ NULL
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ string(5) "query"
+ ["fragment"]=>
+ string(8) "fragment"
+}
+string(15) "?query#fragment"
+string(15) "?query#fragment"
diff --git a/ext/uri/tests/rfc3986/parsing/path_success_slash_only.phpt b/ext/uri/tests/rfc3986/parsing/path_success_slash_only.phpt
new file mode 100644
index 00000000000..e743869570e
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/path_success_slash_only.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - path - slash only
+--FILE--
+<?php
+
+$uri = new Uri\Rfc3986\Uri("https://example.com/");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(20) "https://example.com/"
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/rfc3986/parsing/port_error_multibyte.phpt b/ext/uri/tests/rfc3986/parsing/port_error_multibyte.phpt
new file mode 100644
index 00000000000..54bcd849e5f
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/port_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - port - multibyte character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://example.com:Ȏ");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/port_error_negative.phpt b/ext/uri/tests/rfc3986/parsing/port_error_negative.phpt
new file mode 100644
index 00000000000..df29c2767f0
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/port_error_negative.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - port - negative value
+--FILE--
+<?php
+
+try {
+ var_dump(new Uri\Rfc3986\Uri("http://example.com:-1"));
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/port_error_percent_encoded.phpt b/ext/uri/tests/rfc3986/parsing/port_error_percent_encoded.phpt
new file mode 100644
index 00000000000..e38ad85b575
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/port_error_percent_encoded.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - port - percent encoded character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("https://example.com:%30");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/port_error_user_info_wrong_place.phpt b/ext/uri/tests/rfc3986/parsing/port_error_user_info_wrong_place.phpt
new file mode 100644
index 00000000000..d2632a6f280
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/port_error_user_info_wrong_place.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - port - user info component is in wrong place
+--FILE--
+<?php
+
+try {
+ var_dump(new Uri\Rfc3986\Uri("https://example.com:8080@username:password"));
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/port_success_empty.phpt b/ext/uri/tests/rfc3986/parsing/port_success_empty.phpt
new file mode 100644
index 00000000000..d007625ae8b
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/port_success_empty.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - port - empty
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("http://example.com:");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(19) "http://example.com:"
+string(19) "http://example.com:"
diff --git a/ext/uri/tests/rfc3986/parsing/port_success_zero.phpt b/ext/uri/tests/rfc3986/parsing/port_success_zero.phpt
new file mode 100644
index 00000000000..5d115a255c9
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/port_success_zero.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - port - zero
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("http://example.com:0");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ int(0)
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(20) "http://example.com:0"
+string(20) "http://example.com:0"
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_error_empty.phpt b/ext/uri/tests/rfc3986/parsing/scheme_error_empty.phpt
new file mode 100644
index 00000000000..ba686e7fafc
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_error_empty.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - empty
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_error_multibyte.phpt b/ext/uri/tests/rfc3986/parsing/scheme_error_multibyte.phpt
new file mode 100644
index 00000000000..f638ca80e0c
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - multibyte characters
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("ƕŢŢƤƨ://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_error_percent_encoded.phpt b/ext/uri/tests/rfc3986/parsing/scheme_error_percent_encoded.phpt
new file mode 100644
index 00000000000..d4dba6f260a
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_error_percent_encoded.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - percent encoded character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("http%2F://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_error_reserved.phpt b/ext/uri/tests/rfc3986/parsing/scheme_error_reserved.phpt
new file mode 100644
index 00000000000..99dfb664ea0
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_error_reserved.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - reserved character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("http&://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_success_empty_host.phpt b/ext/uri/tests/rfc3986/parsing/scheme_success_empty_host.phpt
new file mode 100644
index 00000000000..c0110735db8
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_success_empty_host.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - empty host
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https:example.com");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(11) "example.com"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(17) "https:example.com"
+string(17) "https:example.com"
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_success_iana.phpt b/ext/uri/tests/rfc3986/parsing/scheme_success_iana.phpt
new file mode 100644
index 00000000000..e054e258b63
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_success_iana.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - IANA scheme
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("chrome-extension://example.com");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(16) "chrome-extension"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(30) "chrome-extension://example.com"
+string(30) "chrome-extension://example.com"
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_success_only.phpt b/ext/uri/tests/rfc3986/parsing/scheme_success_only.phpt
new file mode 100644
index 00000000000..a898c801272
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_success_only.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - only scheme
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("http:");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(5) "http:"
+string(5) "http:"
diff --git a/ext/uri/tests/rfc3986/parsing/scheme_success_only2.phpt b/ext/uri/tests/rfc3986/parsing/scheme_success_only2.phpt
new file mode 100644
index 00000000000..65c52faafcf
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/scheme_success_only2.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - scheme - only scheme
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("http:/");
+
+var_dump($uri);
+var_dump($uri->toRawString());
+var_dump($uri->toString());
+
+?>
+--EXPECTF--
+object(Uri\Rfc3986\Uri)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(6) "http:/"
+string(6) "http:/"
diff --git a/ext/uri/tests/rfc3986/parsing/userinfo_error_extended_ascii.phpt b/ext/uri/tests/rfc3986/parsing/userinfo_error_extended_ascii.phpt
new file mode 100644
index 00000000000..61ea8ecc3c2
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/userinfo_error_extended_ascii.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - userinfo - extended ASCII character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("http://úzör@example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/userinfo_error_multibyte.phpt b/ext/uri/tests/rfc3986/parsing/userinfo_error_multibyte.phpt
new file mode 100644
index 00000000000..fbceea77b1c
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/userinfo_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - userinfo - multibyte character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("http://usĕr:pąss@example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/rfc3986/parsing/userinfo_error_reserved.phpt b/ext/uri/tests/rfc3986/parsing/userinfo_error_reserved.phpt
new file mode 100644
index 00000000000..3b6caeb7a0e
--- /dev/null
+++ b/ext/uri/tests/rfc3986/parsing/userinfo_error_reserved.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\Rfc3986\Uri parsing - userinfo - reserved character
+--FILE--
+<?php
+
+try {
+ new Uri\Rfc3986\Uri("http://us[er]:pass@example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\InvalidUriException: The specified URI is malformed
diff --git a/ext/uri/tests/whatwg/parsing/basic_error_empty.phpt b/ext/uri/tests/whatwg/parsing/basic_error_empty.phpt
new file mode 100644
index 00000000000..2b7114baa73
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_error_empty.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - empty string
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/basic_error_null_byte1.phpt b/ext/uri/tests/whatwg/parsing/basic_error_null_byte1.phpt
new file mode 100644
index 00000000000..db5cd075a6f
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_error_null_byte1.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - URL contains null byte
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://exam\0ple.com");
+} catch (ValueError $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+ValueError: Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes
diff --git a/ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt b/ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt
new file mode 100644
index 00000000000..6aa631c74e9
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - URL contains null byte
+--FILE--
+<?php
+
+try {
+ Uri\WhatWg\Url::parse("https://exam\0ple.com");
+} catch (ValueError $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+ValueError: Uri\WhatWg\Url::parse(): Argument #1 ($uri) must not contain any null bytes
diff --git a/ext/uri/tests/whatwg/parsing/basic_success_all.phpt b/ext/uri/tests/whatwg/parsing/basic_success_all.phpt
new file mode 100644
index 00000000000..0af4d16e4cb
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_success_all.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - all components
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://user:info@example.com:443/foo/bar?abc=123&def=ghi#hashmark");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(4) "info"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(8) "/foo/bar"
+ ["query"]=>
+ string(15) "abc=123&def=ghi"
+ ["fragment"]=>
+ string(8) "hashmark"
+}
+string(62) "https://user:info@example.com/foo/bar?abc=123&def=ghi#hashmark"
diff --git a/ext/uri/tests/whatwg/parsing/basic_sucess_email.phpt b/ext/uri/tests/whatwg/parsing/basic_sucess_email.phpt
new file mode 100644
index 00000000000..dcceb441c67
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_sucess_email.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - mailto email
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("mailto:user@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(6) "mailto"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(16) "user@example.com"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(23) "mailto:user@example.com"
diff --git a/ext/uri/tests/whatwg/parsing/basic_sucess_file.phpt b/ext/uri/tests/whatwg/parsing/basic_sucess_file.phpt
new file mode 100644
index 00000000000..96b67bc24b7
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_sucess_file.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - file
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("file:///E:/Documents%20and%20Settings");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "file"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(0) ""
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(30) "/E:/Documents%20and%20Settings"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(37) "file:///E:/Documents%20and%20Settings"
diff --git a/ext/uri/tests/whatwg/parsing/basic_sucess_urn.phpt b/ext/uri/tests/whatwg/parsing/basic_sucess_urn.phpt
new file mode 100644
index 00000000000..91fe664fb68
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_sucess_urn.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - basic - URN
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(3) "urn"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ NULL
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(41) "uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(45) "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66"
diff --git a/ext/uri/tests/whatwg/parsing/host_error_Invalid.phpt b/ext/uri/tests/whatwg/parsing/host_error_Invalid.phpt
new file mode 100644
index 00000000000..8444ee9e3e9
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_error_Invalid.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - invalid code point
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://ex[a]mple.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (DomainInvalidCodePoint)
diff --git a/ext/uri/tests/whatwg/parsing/host_error_empty1.phpt b/ext/uri/tests/whatwg/parsing/host_error_empty1.phpt
new file mode 100644
index 00000000000..92b317078eb
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_error_empty1.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - empty
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (HostMissing)
diff --git a/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt b/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt
new file mode 100644
index 00000000000..93418545564
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - empty
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://user:pass@");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed
diff --git a/ext/uri/tests/whatwg/parsing/host_error_empty3.phpt b/ext/uri/tests/whatwg/parsing/host_error_empty3.phpt
new file mode 100644
index 00000000000..90f2738148f
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_error_empty3.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - empty
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("/");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/host_error_ipvfuture.phpt b/ext/uri/tests/whatwg/parsing/host_error_ipvfuture.phpt
new file mode 100644
index 00000000000..959aa0c4442
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_error_ipvfuture.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - IPvFuture
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://[v7.host]");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (Ipv6InvalidCodePoint)
diff --git a/ext/uri/tests/whatwg/parsing/host_success_empty4.phpt b/ext/uri/tests/whatwg/parsing/host_success_empty4.phpt
new file mode 100644
index 00000000000..25a28a9750c
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_success_empty4.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - empty
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("///");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/host_success_ipv4.phpt b/ext/uri/tests/whatwg/parsing/host_success_ipv4.phpt
new file mode 100644
index 00000000000..f47a43aa835
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_success_ipv4.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - IPv4
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://192.168.0.1");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "192.168.0.1"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(20) "https://192.168.0.1/"
diff --git a/ext/uri/tests/whatwg/parsing/host_success_ipv4_wrong.phpt b/ext/uri/tests/whatwg/parsing/host_success_ipv4_wrong.phpt
new file mode 100644
index 00000000000..9096bfec3d6
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_success_ipv4_wrong.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - wrong IPv4 format
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://192.168");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "192.0.0.168"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(20) "https://192.0.0.168/"
diff --git a/ext/uri/tests/whatwg/parsing/host_success_ipv6.phpt b/ext/uri/tests/whatwg/parsing/host_success_ipv6.phpt
new file mode 100644
index 00000000000..8cbe42f23ab
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_success_ipv6.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - IPv6
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://[2001:0db8:3333:4444:5555:6666:7777:8888]");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(40) "[2001:db8:3333:4444:5555:6666:7777:8888]"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(49) "https://[2001:db8:3333:4444:5555:6666:7777:8888]/"
diff --git a/ext/uri/tests/whatwg/parsing/host_success_multibyte.phpt b/ext/uri/tests/whatwg/parsing/host_success_multibyte.phpt
new file mode 100644
index 00000000000..15613a1a0a9
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/host_success_multibyte.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - host - multibyte codepoint
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://exḁmple.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+var_dump($url->toUnicodeString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(19) "xn--exmple-xf7b.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(28) "https://xn--exmple-xf7b.com/"
+string(22) "https://exḁmple.com/"
diff --git a/ext/uri/tests/whatwg/parsing/password_success_empty.phpt b/ext/uri/tests/whatwg/parsing/password_success_empty.phpt
new file mode 100644
index 00000000000..adfa82357c2
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/password_success_empty.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - password - empty
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://user:@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(0) ""
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(25) "https://user@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/password_success_extended_ascii.phpt b/ext/uri/tests/whatwg/parsing/password_success_extended_ascii.phpt
new file mode 100644
index 00000000000..02ccf71c6f6
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/password_success_extended_ascii.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - password - extended ASCII character
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("http://user:pásswörd@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(18) "p%C3%A1ssw%C3%B6rd"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(43) "http://user:p%C3%A1ssw%C3%B6rd@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/password_success_multibyte.phpt b/ext/uri/tests/whatwg/parsing/password_success_multibyte.phpt
new file mode 100644
index 00000000000..96d636ea284
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/password_success_multibyte.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - password - multibyte codepoint
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("http://user:pąss@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(9) "p%C4%85ss"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(34) "http://user:p%C4%85ss@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/password_success_percent_encode_set.phpt b/ext/uri/tests/whatwg/parsing/password_success_percent_encode_set.phpt
new file mode 100644
index 00000000000..3a104bd6340
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/password_success_percent_encode_set.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - password - codepoint in percent-encode set
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("http://user:pa|s@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ string(4) "user"
+ ["password"]=>
+ string(6) "pa%7Cs"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(31) "http://user:pa%7Cs@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/path_error_relative_reference.phpt b/ext/uri/tests/whatwg/parsing/path_error_relative_reference.phpt
new file mode 100644
index 00000000000..8e6c6fd3842
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_error_relative_reference.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - relative reference
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("foo");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/path_error_relative_reference_absolute.phpt b/ext/uri/tests/whatwg/parsing/path_error_relative_reference_absolute.phpt
new file mode 100644
index 00000000000..01d549210d9
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_error_relative_reference_absolute.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - relative reference
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("/foo");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/path_error_relative_reference_only_query_fragment.phpt b/ext/uri/tests/whatwg/parsing/path_error_relative_reference_only_query_fragment.phpt
new file mode 100644
index 00000000000..9c6a7a7906b
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_error_relative_reference_only_query_fragment.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - relative reference
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("?query#fragment");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/path_success_multibyte.phpt b/ext/uri/tests/whatwg/parsing/path_success_multibyte.phpt
new file mode 100644
index 00000000000..d89db40fcb3
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_success_multibyte.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - multibyte
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://example.com/fȎȎ");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(14) "/f%C8%8E%C8%8E"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(33) "https://example.com/f%C8%8E%C8%8E"
diff --git a/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt b/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt
new file mode 100644
index 00000000000..9419c817fbb
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - codepoint in percent-encode set
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://example.com/foo\"/<bar>/^{baz}");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(28) "/foo%22/%3Cbar%3E/^%7Bbaz%7D"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(47) "https://example.com/foo%22/%3Cbar%3E/^%7Bbaz%7D"
diff --git a/ext/uri/tests/whatwg/parsing/path_success_slash_only.phpt b/ext/uri/tests/whatwg/parsing/path_success_slash_only.phpt
new file mode 100644
index 00000000000..66bd4e24e0f
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_success_slash_only.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - slash only
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://example.com/");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/path_success_special.phpt b/ext/uri/tests/whatwg/parsing/path_success_special.phpt
new file mode 100644
index 00000000000..33feecd69ee
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/path_success_special.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - path - special characters
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://example.com/fo[o/ba]r/baz=q@z,&");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(20) "/fo[o/ba]r/baz=q@z,&"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(39) "https://example.com/fo[o/ba]r/baz=q@z,&"
diff --git a/ext/uri/tests/whatwg/parsing/port_error_multibyte.phpt b/ext/uri/tests/whatwg/parsing/port_error_multibyte.phpt
new file mode 100644
index 00000000000..8175f2ca992
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/port_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - port - multibyte
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://example.com:Ȏ");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (PortInvalid)
diff --git a/ext/uri/tests/whatwg/parsing/port_error_negative.phpt b/ext/uri/tests/whatwg/parsing/port_error_negative.phpt
new file mode 100644
index 00000000000..bb23476db57
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/port_error_negative.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - port - negative value
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("http://example.com:-1");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (PortInvalid)
diff --git a/ext/uri/tests/whatwg/parsing/port_error_percent_encoded.phpt b/ext/uri/tests/whatwg/parsing/port_error_percent_encoded.phpt
new file mode 100644
index 00000000000..874211ccb1c
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/port_error_percent_encoded.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - port - percent encoded character
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("https://example.com:%30");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (PortInvalid)
diff --git a/ext/uri/tests/whatwg/parsing/port_error_user_info_wrong_place.phpt b/ext/uri/tests/whatwg/parsing/port_error_user_info_wrong_place.phpt
new file mode 100644
index 00000000000..f0f79670230
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/port_error_user_info_wrong_place.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - port - user info component is in wrong place
+--FILE--
+<?php
+
+try {
+ var_dump(new Uri\WhatWg\Url("https://example.com:8080@username:password"));
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (PortInvalid)
diff --git a/ext/uri/tests/whatwg/parsing/port_success_empty.phpt b/ext/uri/tests/whatwg/parsing/port_success_empty.phpt
new file mode 100644
index 00000000000..85ba06f0ba7
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/port_success_empty.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - port - empty
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("http://example.com:");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(19) "http://example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/port_success_zero.phpt b/ext/uri/tests/whatwg/parsing/port_success_zero.phpt
new file mode 100644
index 00000000000..22b5b89427a
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/port_success_zero.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - port - zero
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("http://example.com:0");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ int(0)
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(21) "http://example.com:0/"
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_empty.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_empty.phpt
new file mode 100644
index 00000000000..023c1d3fab9
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_empty.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - empty
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_invalid.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_invalid.phpt
new file mode 100644
index 00000000000..9c69b08161e
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_invalid.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - invalid character
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("http&://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_multibyte.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_multibyte.phpt
new file mode 100644
index 00000000000..0d6601e718e
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_multibyte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - multibyte codepoint
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("ƕŢŢƤƨ://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_only1.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_only1.phpt
new file mode 100644
index 00000000000..92832f65a01
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_only1.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - only scheme
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("http:");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (HostMissing)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_only2.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_only2.phpt
new file mode 100644
index 00000000000..22ab6fc5351
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_only2.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - only scheme
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("http:/");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (HostMissing)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_percent_encoded.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_percent_encoded.phpt
new file mode 100644
index 00000000000..a0324edd370
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_percent_encoded.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - percent encoded character
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("http%2F://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_special.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_special.phpt
new file mode 100644
index 00000000000..9966ad6248e
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_error_special.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - invalid character
+--FILE--
+<?php
+
+try {
+ new Uri\WhatWg\Url("hÁttp://example.com");
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
diff --git a/ext/uri/tests/whatwg/parsing/scheme_success_iana.phpt b/ext/uri/tests/whatwg/parsing/scheme_success_iana.phpt
new file mode 100644
index 00000000000..a59888f5a1e
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_success_iana.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - IANA scheme
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("chrome-extension://example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(16) "chrome-extension"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(0) ""
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(30) "chrome-extension://example.com"
diff --git a/ext/uri/tests/whatwg/parsing/scheme_success_without_slash.phpt b/ext/uri/tests/whatwg/parsing/scheme_success_without_slash.phpt
new file mode 100644
index 00000000000..ab3da6438ce
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/scheme_success_without_slash.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - scheme - without //
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https:example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ NULL
+ ["password"]=>
+ NULL
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(20) "https://example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/username_success_empty.phpt b/ext/uri/tests/whatwg/parsing/username_success_empty.phpt
new file mode 100644
index 00000000000..804fe1feaae
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/username_success_empty.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - username - empty
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://:pass@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(0) ""
+ ["password"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(26) "https://:pass@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/username_success_extended_ascii.phpt b/ext/uri/tests/whatwg/parsing/username_success_extended_ascii.phpt
new file mode 100644
index 00000000000..54e6de85cd6
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/username_success_extended_ascii.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - username - extended ASCII character
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://úzör@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(14) "%C3%BAz%C3%B6r"
+ ["password"]=>
+ string(0) ""
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(35) "https://%C3%BAz%C3%B6r@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/username_success_multibyte.phpt b/ext/uri/tests/whatwg/parsing/username_success_multibyte.phpt
new file mode 100644
index 00000000000..bf08c404d04
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/username_success_multibyte.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - username - multibyte codepoint
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("https://usĕr:pass@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(5) "https"
+ ["username"]=>
+ string(9) "us%C4%95r"
+ ["password"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(35) "https://us%C4%95r:pass@example.com/"
diff --git a/ext/uri/tests/whatwg/parsing/username_success_percent_encode_set.phpt b/ext/uri/tests/whatwg/parsing/username_success_percent_encode_set.phpt
new file mode 100644
index 00000000000..d4aeaf61d72
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/username_success_percent_encode_set.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test Uri\WhatWg\Url parsing - username - codepoint in percent-encode set
+--FILE--
+<?php
+
+$url = new Uri\WhatWg\Url("http://us[er]:pass@example.com");
+
+var_dump($url);
+var_dump($url->toAsciiString());
+
+?>
+--EXPECTF--
+object(Uri\WhatWg\Url)#%d (%d) {
+ ["scheme"]=>
+ string(4) "http"
+ ["username"]=>
+ string(10) "us%5Ber%5D"
+ ["password"]=>
+ string(4) "pass"
+ ["host"]=>
+ string(11) "example.com"
+ ["port"]=>
+ NULL
+ ["path"]=>
+ string(1) "/"
+ ["query"]=>
+ NULL
+ ["fragment"]=>
+ NULL
+}
+string(35) "http://us%5Ber%5D:pass@example.com/"