Implement backed enum coercion in http_build_query()

Fixes GH-15650
Closes GH-15704
This commit is contained in:
Ilija Tovilo 2024-09-01 23:37:29 +02:00
parent 7a8767fe62
commit 1b9568d354
No known key found for this signature in database
GPG Key ID: 5050C66BFCD1015A
4 changed files with 58 additions and 1 deletions

3
NEWS
View File

@ -44,6 +44,9 @@ PHP NEWS
. Fixed bug GH-15711 (SoapClient can't convert BackedEnum to scalar value).
(nielsdos)
- Standard:
. Add support for backed enums in http_build_query(). (ilutov)
12 Sep 2024, PHP 8.4.0beta5
- BCMath:

View File

@ -225,6 +225,7 @@ PHP 8.4 UPGRADE NOTES
. php_uname() now throws ValueErrors on invalid inputs.
. The "allowed_classes" option for unserialize() now throws TypeErrors and
ValueErrors if it is not an array of class names.
. http_build_query() now correctly handles backed enums.
- Tidy:
. Failures in the constructor now throw exceptions rather than emitting

View File

@ -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));

View File

@ -0,0 +1,36 @@
--TEST--
GH-15650: http_build_query() with enum
--FILE--
<?php
enum E1: string {
case C = 'hello world!';
}
enum E2: int {
case C = 42;
}
enum E3 {
case C;
}
echo http_build_query(['e1' => 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