Commit 2b9b40779a2 for php.net

commit 2b9b40779a2ad05a1eb164f86ac6327234f1003f
Author: Máté Kocsis <kocsismate@woohoolabs.com>
Date:   Wed Sep 16 21:14:26 2026 +0200

    ext/uri: Minor fixes for Uri\WhatWg\UrlBuilder

    - Memory allocation errors would trigger an Error
    - Various style fixes
    - Test improvements

diff --git a/ext/uri/tests/whatwg/builder/build_success_soft_errors_reset.phpt b/ext/uri/tests/whatwg/builder/build_success_soft_errors_reset.phpt
index 4bfab1fd57e..80d0da8f5b0 100644
--- a/ext/uri/tests/whatwg/builder/build_success_soft_errors_reset.phpt
+++ b/ext/uri/tests/whatwg/builder/build_success_soft_errors_reset.phpt
@@ -10,6 +10,8 @@
 $softErrors = [];
 $builder->build(softErrors: $softErrors);

+var_dump($softErrors);
+
 $builder->setFragment("ab");
 $url = $builder->build(softErrors: $softErrors);

@@ -20,6 +22,17 @@

 ?>
 --EXPECTF--
+array(1) {
+  [0]=>
+  object(Uri\WhatWg\UrlValidationError)#%d (%d) {
+    ["context"]=>
+    string(2) "	b"
+    ["type"]=>
+    enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
+    ["failure"]=>
+    bool(false)
+  }
+}
 string(23) "https://example.com/#ab"
 object(Uri\WhatWg\Url)#%d (%d) {
   ["scheme"]=>
diff --git a/ext/uri/tests/whatwg/parsing/basic_error_soft_errors_unchanged.phpt b/ext/uri/tests/whatwg/parsing/basic_error_soft_errors_unchanged.phpt
new file mode 100644
index 00000000000..2f7549f4757
--- /dev/null
+++ b/ext/uri/tests/whatwg/parsing/basic_error_soft_errors_unchanged.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Test Uri\WhatWg\Url::__construct() - error - leaves soft errors unchanged
+--FILE--
+<?php
+
+$softErrors = ["unchanged"];
+
+try {
+    new Uri\WhatWg\Url("🐘", softErrors: $softErrors);
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->getMessage(), "\n";
+    var_dump($e->errors);
+}
+
+var_dump($softErrors);
+
+?>
+--EXPECTF--
+Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)
+array(1) {
+  [0]=>
+  object(Uri\WhatWg\UrlValidationError)#%d (%d) {
+    ["context"]=>
+    string(4) "🐘"
+    ["type"]=>
+    enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl)
+    ["failure"]=>
+    bool(true)
+  }
+}
+array(1) {
+  [0]=>
+  string(9) "unchanged"
+}
diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c
index 0e5d9e8a66f..3ae5a3890e8 100644
--- a/ext/uri/uri_parser_whatwg.c
+++ b/ext/uri/uri_parser_whatwg.c
@@ -736,9 +736,9 @@ static void php_uri_parser_whatwg_destroy(void *uri)
 	lxb_url_destroy(lexbor_uri);
 }

-ZEND_ATTRIBUTE_NONNULL static void php_uri_parser_whatwg_throw_exception(const char *message)
+ZEND_ATTRIBUTE_NONNULL static void php_uri_parser_whatwg_throw_exception(zend_class_entry *exception_ce, const char *message)
 {
-	zend_object *exception = zend_throw_exception(php_uri_ce_whatwg_invalid_url_exception, message, 0);
+	zend_object *exception = zend_throw_exception(exception_ce, message, 0);
 	zval errors;
 	ZVAL_EMPTY_ARRAY(&errors);
 	zend_update_property(exception->ce, exception, ZEND_STRL("errors"), &errors);
@@ -993,7 +993,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh

 	lxb_url_t *lexbor_url = lexbor_mraw_calloc(lexbor_parser.mraw, sizeof(*lexbor_url));
 	if (lexbor_url == NULL) {
-		php_uri_parser_whatwg_throw_exception("Memory allocation error");
+		php_uri_parser_whatwg_throw_exception(php_uri_ce_error, "Memory allocation error");
 		return NULL;
 	}

@@ -1026,17 +1026,17 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
 		|| lexbor_url->host.type == LXB_URL_HOST_TYPE_EMPTY
 		|| lexbor_url->scheme.type == LXB_URL_SCHEMEL_TYPE_FILE) {
 		if (Z_TYPE_P(username) != IS_NULL) {
-			php_uri_parser_whatwg_throw_exception("The specified URL cannot have username");
+			php_uri_parser_whatwg_throw_exception(php_uri_ce_whatwg_invalid_url_exception, "The specified URL cannot have username");
 			goto failure;
 		}

 		if (Z_TYPE_P(password) != IS_NULL) {
-			php_uri_parser_whatwg_throw_exception("The specified URL cannot have password");
+			php_uri_parser_whatwg_throw_exception(php_uri_ce_whatwg_invalid_url_exception, "The specified URL cannot have password");
 			goto failure;
 		}

 		if (Z_TYPE_P(port) != IS_NULL) {
-			php_uri_parser_whatwg_throw_exception("The specified URL cannot have port");
+			php_uri_parser_whatwg_throw_exception(php_uri_ce_whatwg_invalid_url_exception, "The specified URL cannot have port");
 			goto failure;
 		}
 	}
@@ -1075,10 +1075,10 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
 		lexbor_str_init(&lexbor_url->query, lexbor_url->mraw, 1);
 	} else {
 		result = php_uri_parser_whatwg_query_write(lexbor_url, query, NULL);
-	}
-	php_uri_parser_whatwg_build_errors(&errors);
-	if (result == FAILURE) {
-		goto failure;
+		php_uri_parser_whatwg_build_errors(&errors);
+		if (result == FAILURE) {
+			goto failure;
+		}
 	}

 	if (Z_TYPE_P(fragment) == IS_STRING && Z_STRLEN_P(fragment) == 0) {
@@ -1088,10 +1088,10 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
 		lexbor_str_init(&lexbor_url->fragment, lexbor_url->mraw, 1);
 	} else {
 		result = php_uri_parser_whatwg_fragment_write(lexbor_url, fragment, NULL);
-	}
-	php_uri_parser_whatwg_build_errors(&errors);
-	if (result == FAILURE) {
-		goto failure;
+		php_uri_parser_whatwg_build_errors(&errors);
+		if (result == FAILURE) {
+			goto failure;
+		}
 	}

 	if (lexbor_base_url != NULL) {
@@ -1107,16 +1107,17 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
 	return lexbor_url;

 failure:
+	ZEND_ASSERT(EG(exception));
+
 	/* Include errors from earlier components in the exception raised by a later component. */
-	if (zend_hash_num_elements(Z_ARRVAL(errors)) > 0 && EG(exception)
+	if (zend_hash_num_elements(Z_ARRVAL(errors)) > 0
 		&& instanceof_function(EG(exception)->ce, php_uri_ce_whatwg_invalid_url_exception)) {
 		zval rv;
 		zval *exception_errors = zend_read_property(php_uri_ce_whatwg_invalid_url_exception,
 			EG(exception), ZEND_STRL("errors"), true, &rv);
 		ZEND_ASSERT(Z_TYPE_P(exception_errors) == IS_ARRAY);

-		zval *error;
-		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(exception_errors), error) {
+		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(exception_errors), zval *error) {
 			Z_TRY_ADDREF_P(error);
 			zend_hash_next_index_insert(Z_ARRVAL(errors), error);
 		} ZEND_HASH_FOREACH_END();