Commit 1c2ae570dc1 for php.net
commit 1c2ae570dc18cdfca4cc9d1c94a38f10cb76f873
Author: Máté Kocsis <kocsismate@woohoolabs.com>
Date: Tue Sep 8 21:32:47 2026 +0200
ext/uri: Initialize builder validation exception errors
Populate validation error details when a code is available, and initialize an empty errors array for other builder failures.
diff --git a/ext/uri/tests/whatwg/builder/build_error_initialized_errors.phpt b/ext/uri/tests/whatwg/builder/build_error_initialized_errors.phpt
new file mode 100644
index 00000000000..f3da52798c5
--- /dev/null
+++ b/ext/uri/tests/whatwg/builder/build_error_initialized_errors.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Test Uri\WhatWg\UrlBuilder::setHost() - error - initializes validation errors
+--FILE--
+<?php
+
+$builder = new Uri\WhatWg\UrlBuilder();
+
+try {
+ $builder->setHost("example.com/path");
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+ var_dump($e->errors);
+}
+
+?>
+--EXPECTF--
+Uri\WhatWg\InvalidUrlException: The specified host is malformed (HostInvalidCodePoint)
+array(1) {
+ [0]=>
+ object(Uri\WhatWg\UrlValidationError)#%d (%d) {
+ ["context"]=>
+ string(0) ""
+ ["type"]=>
+ enum(Uri\WhatWg\UrlValidationErrorType::HostInvalidCodePoint)
+ ["failure"]=>
+ bool(true)
+ }
+}
diff --git a/ext/uri/tests/whatwg/builder/build_error_initialized_global_errors.phpt b/ext/uri/tests/whatwg/builder/build_error_initialized_global_errors.phpt
new file mode 100644
index 00000000000..3c25356b3db
--- /dev/null
+++ b/ext/uri/tests/whatwg/builder/build_error_initialized_global_errors.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Test Uri\WhatWg\UrlBuilder::build() - error - initializes errors for invalid component combinations
+--FILE--
+<?php
+
+$builder = new Uri\WhatWg\UrlBuilder();
+$builder->setScheme("foo");
+$builder->setUsername("user");
+
+try {
+ $builder->build();
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+ var_dump($e->errors);
+}
+
+?>
+--EXPECT--
+Uri\WhatWg\InvalidUrlException: The specified URL cannot have username
+array(0) {
+}
diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c
index 1c347471796..c07e8fae900 100644
--- a/ext/uri/uri_parser_whatwg.c
+++ b/ext/uri/uri_parser_whatwg.c
@@ -148,6 +148,23 @@ ZEND_ATTRIBUTE_NONNULL static bool get_reason_from_error_type(const lxb_url_erro
}
}
+ZEND_ATTRIBUTE_NONNULL static bool append_validation_error(
+ HashTable *errors, lxb_url_error_type_t error_type, const char *context, const char **reason
+) {
+ zval error;
+ object_init_ex(&error, php_uri_ce_whatwg_url_validation_error);
+ zend_update_property_string(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZEND_STRL("context"), context);
+
+ bool failure = get_reason_from_error_type(error_type, reason);
+ zval type;
+ ZVAL_OBJ(&type, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_validation_error_type, *reason));
+ zend_update_property_ex(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &type);
+ zend_update_property_bool(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZEND_STRL("failure"), failure);
+ zend_hash_next_index_insert(errors, &error);
+
+ return failure;
+}
+
/**
* Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in
* https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum.
@@ -159,26 +176,10 @@ ZEND_ATTRIBUTE_NONNULL static const char *fill_errors_inner(HashTable *errors)
lexbor_plog_entry_t *lxb_error;
while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) {
- zval error;
- object_init_ex(&error, php_uri_ce_whatwg_url_validation_error);
- zend_update_property_string(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data);
-
- const char *error_str;
- zval failure;
-
- ZVAL_BOOL(&failure, get_reason_from_error_type(lxb_error->id, &error_str));
-
- zval error_type;
- ZVAL_OBJ(&error_type, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_validation_error_type, error_str));
- zend_update_property_ex(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type);
-
- zend_update_property(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZEND_STRL("failure"), &failure);
-
- if (Z_TYPE(failure) == IS_TRUE) {
- result = error_str;
+ const char *reason;
+ if (append_validation_error(errors, lxb_error->id, (const char *) lxb_error->data, &reason)) {
+ result = reason;
}
-
- zend_hash_next_index_insert(errors, &error);
}
return result;
@@ -735,22 +736,31 @@ 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_object *exception = zend_throw_exception(php_uri_ce_whatwg_invalid_url_exception, message, 0);
+ zval errors;
+ ZVAL_EMPTY_ARRAY(&errors);
+ zend_update_property(exception->ce, exception, ZEND_STRL("errors"), &errors);
+}
+
ZEND_ATTRIBUTE_NONNULL static zend_always_inline zend_result php_uri_parser_whatwg_component_error(
const char *component_name, const lxb_url_error_type_t error_type
) {
- const char *reason = "";
+ zval errors;
+ array_init(&errors);
+
+ const char *reason = NULL;
if (error_type != LXB_URL_ERROR_TYPE__LAST_ENTRY) {
- get_reason_from_error_type(error_type, &reason);
+ append_validation_error(Z_ARRVAL(errors), error_type, "", &reason);
}
- zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception,
- 0,
- "The specified %s is malformed%s%s%s",
- component_name,
- reason ? " (" : "",
- reason ? reason : "",
- reason ? ")" : ""
- );
+ zend_object *exception = zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception,
+ 0, "The specified %s is malformed%s%s%s", component_name,
+ reason ? " (" : "", reason ? reason : "", reason ? ")" : "");
+
+ zend_update_property(exception->ce, exception, ZEND_STRL("errors"), &errors);
+ zval_ptr_dtor(&errors);
return FAILURE;
}
@@ -984,17 +994,17 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
php_uri_parser_whatwg_get_special_scheme(Z_STR_P(scheme)) == LXB_URL_SCHEMEL_TYPE_FILE
) {
if (Z_TYPE_P(username) != IS_NULL) {
- zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception, 0, "The specified URL cannot have username");
+ php_uri_parser_whatwg_throw_exception("The specified URL cannot have username");
return NULL;
}
if (Z_TYPE_P(password) != IS_NULL) {
- zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception, 0, "The specified URL cannot have password");
+ php_uri_parser_whatwg_throw_exception("The specified URL cannot have password");
return NULL;
}
if (Z_TYPE_P(port) != IS_NULL) {
- zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception, 0, "The specified URL cannot have port");
+ php_uri_parser_whatwg_throw_exception("The specified URL cannot have port");
return NULL;
}
}
@@ -1003,7 +1013,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) {
- zend_throw_exception(php_uri_ce_whatwg_invalid_url_exception, "Memory allocation error", 0);
+ php_uri_parser_whatwg_throw_exception("Memory allocation error");
return NULL;
}