From 4223d4538f353c5041b8c5b701e5cc1bd9f20fb2 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 1 Sep 2024 23:37:29 +0200 Subject: [PATCH] Implement backed enum coercion in http_build_query() Fixes GH-15650 --- ext/standard/http.c | 19 ++++++++++++++- ext/standard/tests/http/gh15650.phpt | 36 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/http/gh15650.phpt diff --git a/ext/standard/http.c b/ext/standard/http.c index 5f796a4a4026b..fd862b605a7e6 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -20,6 +20,7 @@ #include "SAPI.h" #include "zend_exceptions.h" #include "basic_functions.h" +#include "zend_enum.h" static void php_url_encode_scalar(zval *scalar, smart_str *form_str, int encoding_type, zend_ulong index_int, @@ -56,6 +57,7 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str, } smart_str_appendc(form_str, '='); +try_again: switch (Z_TYPE_P(scalar)) { case IS_STRING: { zend_string *encoded_data; @@ -90,6 +92,14 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str, case IS_TRUE: smart_str_appendc(form_str, '1'); break; + case IS_OBJECT: + ZEND_ASSERT(Z_OBJCE_P(scalar)->ce_flags & ZEND_ACC_ENUM); + if (Z_OBJCE_P(scalar)->enum_backing_type == IS_UNDEF) { + zend_value_error("Unbacked enum %s cannot be converted to a string", ZSTR_VAL(Z_OBJCE_P(scalar)->name)); + return; + } + scalar = zend_enum_fetch_case_value(Z_OBJ_P(scalar)); + goto try_again; /* All possible types are either handled here or previously */ EMPTY_SWITCH_DEFAULT_CASE(); } @@ -154,7 +164,9 @@ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, } ZVAL_DEREF(zdata); - if (Z_TYPE_P(zdata) == IS_ARRAY || Z_TYPE_P(zdata) == IS_OBJECT) { + if (Z_TYPE_P(zdata) == IS_ARRAY + || (Z_TYPE_P(zdata) == IS_OBJECT + && !(Z_OBJCE_P(zdata)->ce_flags & ZEND_ACC_ENUM))) { zend_string *new_prefix; if (key) { zend_string *encoded_key; @@ -233,6 +245,11 @@ PHP_FUNCTION(http_build_query) Z_PARAM_LONG(enc_type) ZEND_PARSE_PARAMETERS_END(); + if (UNEXPECTED(Z_TYPE_P(formdata) == IS_OBJECT && (Z_OBJCE_P(formdata)->ce_flags & ZEND_ACC_ENUM))) { + zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(formdata)); + RETURN_THROWS(); + } + php_url_encode_hash_ex(HASH_OF(formdata), &formstr, prefix, prefix_len, /* key_prefix */ NULL, (Z_TYPE_P(formdata) == IS_OBJECT ? formdata : NULL), arg_sep, (int)enc_type); RETURN_STR(smart_str_extract(&formstr)); diff --git a/ext/standard/tests/http/gh15650.phpt b/ext/standard/tests/http/gh15650.phpt new file mode 100644 index 0000000000000..aed56a3a99513 --- /dev/null +++ b/ext/standard/tests/http/gh15650.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-15650: Test +--FILE-- + E1::C, 'e2' => E2::C]), "\n"; + +try { + echo http_build_query(['e3' => E3::C]); +} catch (Throwable $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} + +try { + echo http_build_query(E1::C); +} catch (Throwable $e) { + echo get_class($e), ': ', $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +e1=hello+world%21&e2=42 +ValueError: Unbacked enum E3 cannot be converted to a string +TypeError: http_build_query(): Argument #1 ($data) must be of type array, E1 given