Skip to content

Commit 1b9568d

Browse files
committed
Implement backed enum coercion in http_build_query()
Fixes phpGH-15650 Closes phpGH-15704
1 parent 7a8767f commit 1b9568d

File tree

4 files changed

+58
-1
lines changed

4 files changed

+58
-1
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ PHP NEWS
4444
. Fixed bug GH-15711 (SoapClient can't convert BackedEnum to scalar value).
4545
(nielsdos)
4646

47+
- Standard:
48+
. Add support for backed enums in http_build_query(). (ilutov)
49+
4750
12 Sep 2024, PHP 8.4.0beta5
4851

4952
- BCMath:

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ PHP 8.4 UPGRADE NOTES
225225
. php_uname() now throws ValueErrors on invalid inputs.
226226
. The "allowed_classes" option for unserialize() now throws TypeErrors and
227227
ValueErrors if it is not an array of class names.
228+
. http_build_query() now correctly handles backed enums.
228229

229230
- Tidy:
230231
. Failures in the constructor now throw exceptions rather than emitting

ext/standard/http.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "SAPI.h"
2121
#include "zend_exceptions.h"
2222
#include "basic_functions.h"
23+
#include "zend_enum.h"
2324

2425
static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
2526
int encoding_type, zend_ulong index_int,
@@ -56,6 +57,7 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
5657
}
5758
smart_str_appendc(form_str, '=');
5859

60+
try_again:
5961
switch (Z_TYPE_P(scalar)) {
6062
case IS_STRING: {
6163
zend_string *encoded_data;
@@ -90,6 +92,14 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
9092
case IS_TRUE:
9193
smart_str_appendc(form_str, '1');
9294
break;
95+
case IS_OBJECT:
96+
ZEND_ASSERT(Z_OBJCE_P(scalar)->ce_flags & ZEND_ACC_ENUM);
97+
if (Z_OBJCE_P(scalar)->enum_backing_type == IS_UNDEF) {
98+
zend_value_error("Unbacked enum %s cannot be converted to a string", ZSTR_VAL(Z_OBJCE_P(scalar)->name));
99+
return;
100+
}
101+
scalar = zend_enum_fetch_case_value(Z_OBJ_P(scalar));
102+
goto try_again;
93103
/* All possible types are either handled here or previously */
94104
EMPTY_SWITCH_DEFAULT_CASE();
95105
}
@@ -154,7 +164,9 @@ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
154164
}
155165

156166
ZVAL_DEREF(zdata);
157-
if (Z_TYPE_P(zdata) == IS_ARRAY || Z_TYPE_P(zdata) == IS_OBJECT) {
167+
if (Z_TYPE_P(zdata) == IS_ARRAY
168+
|| (Z_TYPE_P(zdata) == IS_OBJECT
169+
&& !(Z_OBJCE_P(zdata)->ce_flags & ZEND_ACC_ENUM))) {
158170
zend_string *new_prefix;
159171
if (key) {
160172
zend_string *encoded_key;
@@ -233,6 +245,11 @@ PHP_FUNCTION(http_build_query)
233245
Z_PARAM_LONG(enc_type)
234246
ZEND_PARSE_PARAMETERS_END();
235247

248+
if (UNEXPECTED(Z_TYPE_P(formdata) == IS_OBJECT && (Z_OBJCE_P(formdata)->ce_flags & ZEND_ACC_ENUM))) {
249+
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(formdata));
250+
RETURN_THROWS();
251+
}
252+
236253
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);
237254

238255
RETURN_STR(smart_str_extract(&formstr));

ext/standard/tests/http/gh15650.phpt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
GH-15650: http_build_query() with enum
3+
--FILE--
4+
<?php
5+
6+
enum E1: string {
7+
case C = 'hello world!';
8+
}
9+
10+
enum E2: int {
11+
case C = 42;
12+
}
13+
14+
enum E3 {
15+
case C;
16+
}
17+
18+
echo http_build_query(['e1' => E1::C, 'e2' => E2::C]), "\n";
19+
20+
try {
21+
echo http_build_query(['e3' => E3::C]);
22+
} catch (Throwable $e) {
23+
echo get_class($e), ': ', $e->getMessage(), "\n";
24+
}
25+
26+
try {
27+
echo http_build_query(E1::C);
28+
} catch (Throwable $e) {
29+
echo get_class($e), ': ', $e->getMessage(), "\n";
30+
}
31+
32+
?>
33+
--EXPECT--
34+
e1=hello+world%21&e2=42
35+
ValueError: Unbacked enum E3 cannot be converted to a string
36+
TypeError: http_build_query(): Argument #1 ($data) must be of type array, E1 given

0 commit comments

Comments
 (0)