Commit c01c9d009c1 for php

commit c01c9d009c1db68a87dabd45b128f544913a63f2
Author: NickSdot <32384907+NickSdot@users.noreply.github.com>
Date:   Wed Oct 7 19:51:24 2026 +0700

    ext/uri: Fixes inconsistent validation ordering (#23823)

    Co-authored-by: Tim Düsterhus <209270+TimWolla@users.noreply.github.com>

diff --git a/UPGRADING b/UPGRADING
index 0e4a5288d68..d426f960abe 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -326,6 +326,10 @@ PHP 8.6 UPGRADE NOTES
     system.

 - URI:
+  . __construct(), parse(), and with*() of Uri\WhatWg\Url, and
+    Uri\WhatWg\UrlBuilder::build() now report validation errors in the order
+    in which they are detected. This reverses the order of multiple errors
+    compared to PHP 8.5.
   . Uri\WhatWg\Url::__construct() now sets $softErrors to an empty array when
     URL processing throws an exception, instead of preserving its value.
     URL validation errors remain available in the
diff --git a/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_with_base.phpt b/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_with_base.phpt
index b1a8b221849..5ac520b2ebc 100644
--- a/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_with_base.phpt
+++ b/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_with_base.phpt
@@ -21,7 +21,7 @@
 ?>
 --EXPECT--
 Uri\WhatWg\InvalidUrlException: The specified path is malformed (MissingSchemeNonRelativeUrl)
-enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl)
 enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
+enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl)
 array(0) {
 }
diff --git a/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_without_base.phpt b/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_without_base.phpt
index f43ab31164b..95f3d690afa 100644
--- a/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_without_base.phpt
+++ b/ext/uri/tests/whatwg/builder/build_error_soft_errors_reset_without_base.phpt
@@ -3,6 +3,14 @@
 --FILE--
 <?php

+$referenceErrors = [];
+
+try {
+    new Uri\WhatWg\Url("ht\ttps://");
+} catch (Throwable $e) {
+    $referenceErrors = $e->errors;
+}
+
 $builder = new Uri\WhatWg\UrlBuilder();
 $builder->setScheme("ht\ttps");
 $builder->setHost(null);
@@ -13,6 +21,10 @@
 } catch (Throwable $e) {
     echo $e::class, ': ', $e->getMessage(), "\n";
     var_dump($e->errors);
+    var_dump(
+        array_map(static fn($error) => $error->type, $e->errors)
+        === array_map(static fn($error) => $error->type, $referenceErrors)
+    );
 }

 var_dump($softErrors);
@@ -40,5 +52,6 @@ enum(Uri\WhatWg\UrlValidationErrorType::HostMissing)
     bool(true)
   }
 }
+bool(true)
 array(0) {
 }
diff --git a/ext/uri/tests/whatwg/builder/build_success_validation_warning_order_without_base.phpt b/ext/uri/tests/whatwg/builder/build_success_validation_warning_order_without_base.phpt
new file mode 100644
index 00000000000..815d48c422c
--- /dev/null
+++ b/ext/uri/tests/whatwg/builder/build_success_validation_warning_order_without_base.phpt
@@ -0,0 +1,67 @@
+--TEST--
+Test Uri\WhatWg\UrlBuilder::build() - success - validation warning order without base URL
+--FILE--
+<?php
+
+$errors = [];
+$referenceErrors = [];
+
+$reference = new Uri\WhatWg\Url('https://127.0.0.1.\newPath', null, $referenceErrors);
+
+$url = new Uri\WhatWg\UrlBuilder()
+    ->setScheme('https')
+    ->setHost('127.0.0.1.')
+    ->setPath('\newPath')
+    ->build(null, $errors);
+
+var_dump($url->toAsciiString());
+var_dump($url);
+var_dump($errors);
+var_dump($errors == $referenceErrors);
+var_dump($url->equals(new Uri\WhatWg\Url($url->toAsciiString())));
+var_dump($url->equals($reference, Uri\UriComparisonMode::IncludeFragment));
+
+?>
+--EXPECTF--
+string(25) "https://127.0.0.1/newPath"
+object(Uri\WhatWg\Url)#%d (%d) {
+  ["scheme"]=>
+  string(5) "https"
+  ["username"]=>
+  NULL
+  ["password"]=>
+  NULL
+  ["host"]=>
+  string(9) "127.0.0.1"
+  ["port"]=>
+  NULL
+  ["path"]=>
+  string(8) "/newPath"
+  ["query"]=>
+  NULL
+  ["fragment"]=>
+  NULL
+}
+array(2) {
+  [0]=>
+  object(Uri\WhatWg\UrlValidationError)#%d (%d) {
+    ["context"]=>
+    string(0) ""
+    ["type"]=>
+    enum(Uri\WhatWg\UrlValidationErrorType::Ipv4EmptyPart)
+    ["failure"]=>
+    bool(false)
+  }
+  [1]=>
+  object(Uri\WhatWg\UrlValidationError)#%d (%d) {
+    ["context"]=>
+    string(8) "\newPath"
+    ["type"]=>
+    enum(Uri\WhatWg\UrlValidationErrorType::InvalidReverseSoldius)
+    ["failure"]=>
+    bool(false)
+  }
+}
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/uri/tests/whatwg/builder/path_success_opaque_spaces.phpt b/ext/uri/tests/whatwg/builder/path_success_opaque_spaces.phpt
index c66d10f29f5..80c30d8a7b8 100644
--- a/ext/uri/tests/whatwg/builder/path_success_opaque_spaces.phpt
+++ b/ext/uri/tests/whatwg/builder/path_success_opaque_spaces.phpt
@@ -39,7 +39,7 @@
   [0]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(1) " "
+    string(6) "  abc "
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
@@ -57,7 +57,7 @@ enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
   [2]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(6) "  abc "
+    string(1) " "
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
diff --git a/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_fragment.phpt b/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_fragment.phpt
index b6d72abded4..f1bd6860c96 100644
--- a/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_fragment.phpt
+++ b/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_fragment.phpt
@@ -40,7 +40,7 @@
   [0]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(2) " #"
+    string(3) "  #"
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
@@ -49,7 +49,7 @@ enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
   [1]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(3) "  #"
+    string(2) " #"
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
diff --git a/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_query.phpt b/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_query.phpt
index d719feafc3d..9a52a7c9765 100644
--- a/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_query.phpt
+++ b/ext/uri/tests/whatwg/builder/path_success_opaque_spaces_with_query.phpt
@@ -40,7 +40,7 @@
   [0]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(2) " ?"
+    string(3) "  ?"
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
@@ -49,7 +49,7 @@ enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
   [1]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(3) "  ?"
+    string(2) " ?"
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
diff --git a/ext/uri/tests/whatwg/modification/withPath_success_file_invalid_drive_letter.phpt b/ext/uri/tests/whatwg/modification/withPath_success_file_invalid_drive_letter.phpt
index 39ed5d30a4f..f57c8b0da2a 100644
--- a/ext/uri/tests/whatwg/modification/withPath_success_file_invalid_drive_letter.phpt
+++ b/ext/uri/tests/whatwg/modification/withPath_success_file_invalid_drive_letter.phpt
@@ -14,8 +14,8 @@
 string(5) "/c:/x"
 array(2) {
   [0]=>
-  string(14) "InvalidUrlUnit"
-  [1]=>
   string(29) "FileInvalidWindowsDriveLetter"
+  [1]=>
+  string(14) "InvalidUrlUnit"
 }
 string(3) "/zz"
diff --git a/ext/uri/tests/whatwg/parsing/basic_success_invalid_url_unit_warnings.phpt b/ext/uri/tests/whatwg/parsing/basic_success_invalid_url_unit_warnings.phpt
index 4ee2a3d3b92..db989449071 100644
--- a/ext/uri/tests/whatwg/parsing/basic_success_invalid_url_unit_warnings.phpt
+++ b/ext/uri/tests/whatwg/parsing/basic_success_invalid_url_unit_warnings.phpt
@@ -35,7 +35,7 @@
   [0]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(1) " "
+    string(21) " https://example.org "
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
@@ -44,7 +44,7 @@ enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
   [1]=>
   object(Uri\WhatWg\UrlValidationError)#%d (%d) {
     ["context"]=>
-    string(21) " https://example.org "
+    string(1) " "
     ["type"]=>
     enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
     ["failure"]=>
diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c
index 4f4f8fcc93b..e0346362423 100644
--- a/ext/uri/uri_parser_whatwg.c
+++ b/ext/uri/uri_parser_whatwg.c
@@ -173,14 +173,17 @@ ZEND_ATTRIBUTE_NONNULL static bool append_validation_error(
 ZEND_ATTRIBUTE_NONNULL static const char *fill_errors_inner(HashTable *errors)
 {
 	const char *result = NULL;
+	lexbor_plog_t *log = lexbor_parser.log;
+	const size_t length = lexbor_plog_length(log);

-	lexbor_plog_entry_t *lxb_error;
-	while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) {
+	for (size_t i = 0; i < length; i++) {
+		const lexbor_plog_entry_t *lxb_error = lexbor_array_obj_get(&log->list, i);
 		const char *reason;
-		if (append_validation_error(errors, lxb_error->id, (const char *) lxb_error->data, &reason)) {
+		if (append_validation_error(errors, lxb_error->id, (const char *) lxb_error->data, &reason) && result == NULL) {
 			result = reason;
 		}
 	}
+	lexbor_plog_clean(log);

 	return result;
 }