Skip to content

Commit 22220db

Browse files
committed
Add C API for getting backed enum case by value
1 parent 1584352 commit 22220db

File tree

6 files changed

+113
-22
lines changed

6 files changed

+113
-22
lines changed

Zend/tests/enum/internal_enums.phpt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,29 @@ var_dump(ZendTestStringEnum::Foo->value);
2121
var_dump($bar = ZendTestStringEnum::from("Test2"));
2222
var_dump($bar === ZendTestStringEnum::Bar);
2323
var_dump(ZendTestStringEnum::tryFrom("Test3"));
24+
var_dump(ZendTestStringEnum::tryFrom(42));
2425
var_dump(ZendTestStringEnum::cases());
2526

2627
var_dump($s = serialize($foo));
2728
var_dump(unserialize($s));
2829
var_dump(unserialize($s) === $foo);
2930

31+
function test_int_enum(int|string $case) {
32+
try {
33+
var_dump(ZendTestIntEnum::from($case));
34+
} catch (\Error $e) {
35+
echo get_class($e) . ': ' . $e->getMessage() . "\n";
36+
}
37+
var_dump(ZendTestIntEnum::tryFrom($case));
38+
}
39+
40+
test_int_enum(1);
41+
test_int_enum('1');
42+
test_int_enum(2);
43+
test_int_enum('2');
44+
test_int_enum(-1);
45+
test_int_enum('-1');
46+
3047
?>
3148
--EXPECT--
3249
enum(ZendTestUnitEnum::Bar)
@@ -47,14 +64,29 @@ string(5) "Test1"
4764
enum(ZendTestStringEnum::Bar)
4865
bool(true)
4966
NULL
50-
array(3) {
67+
enum(ZendTestStringEnum::Qux)
68+
array(4) {
5169
[0]=>
5270
enum(ZendTestStringEnum::Foo)
5371
[1]=>
5472
enum(ZendTestStringEnum::Bar)
5573
[2]=>
5674
enum(ZendTestStringEnum::Baz)
75+
[3]=>
76+
enum(ZendTestStringEnum::Qux)
5777
}
5878
string(30) "E:22:"ZendTestStringEnum:Foo";"
5979
enum(ZendTestStringEnum::Foo)
6080
bool(true)
81+
enum(ZendTestIntEnum::Foo)
82+
enum(ZendTestIntEnum::Foo)
83+
enum(ZendTestIntEnum::Foo)
84+
enum(ZendTestIntEnum::Foo)
85+
ValueError: 2 is not a valid backing value for enum "ZendTestIntEnum"
86+
NULL
87+
ValueError: 2 is not a valid backing value for enum "ZendTestIntEnum"
88+
NULL
89+
enum(ZendTestIntEnum::Baz)
90+
enum(ZendTestIntEnum::Baz)
91+
enum(ZendTestIntEnum::Baz)
92+
enum(ZendTestIntEnum::Baz)

Zend/zend_enum.c

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -207,29 +207,61 @@ static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
207207
} ZEND_HASH_FOREACH_END();
208208
}
209209

210+
ZEND_API zend_object *zend_enum_get_case_by_value(zend_class_entry *ce, zend_long long_key, zend_string *string_key)
211+
{
212+
zval *case_name_zv;
213+
if (ce->enum_backing_type == IS_LONG) {
214+
case_name_zv = zend_hash_index_find(ce->backed_enum_table, long_key);
215+
} else {
216+
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
217+
ZEND_ASSERT(string_key != NULL);
218+
case_name_zv = zend_hash_find(ce->backed_enum_table, string_key);
219+
}
220+
221+
if (case_name_zv == NULL) {
222+
return NULL;
223+
}
224+
225+
// TODO: We might want to store pointers to constants in backed_enum_table instead of names,
226+
// to make this lookup more efficient.
227+
ZEND_ASSERT(Z_TYPE_P(case_name_zv) == IS_STRING);
228+
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), Z_STR_P(case_name_zv));
229+
ZEND_ASSERT(c != NULL);
230+
zval *case_zv = &c->value;
231+
if (Z_TYPE_P(case_zv) == IS_CONSTANT_AST) {
232+
if (zval_update_constant_ex(case_zv, c->ce) == FAILURE) {
233+
return NULL;
234+
}
235+
}
236+
237+
return Z_OBJ_P(case_zv);
238+
}
239+
210240
static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
211241
{
212242
zend_class_entry *ce = execute_data->func->common.scope;
213-
zend_string *string_key;
214-
zend_long long_key;
243+
zend_string *string_key = NULL;
244+
zend_long long_key = 0;
215245

216-
zval *case_name_zv;
217246
if (ce->enum_backing_type == IS_LONG) {
218247
ZEND_PARSE_PARAMETERS_START(1, 1)
219248
Z_PARAM_LONG(long_key)
220249
ZEND_PARSE_PARAMETERS_END();
221-
222-
case_name_zv = zend_hash_index_find(ce->backed_enum_table, long_key);
223250
} else {
224251
ZEND_PARSE_PARAMETERS_START(1, 1)
225252
Z_PARAM_STR(string_key)
226253
ZEND_PARSE_PARAMETERS_END();
227254

228255
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
229-
case_name_zv = zend_hash_find(ce->backed_enum_table, string_key);
230256
}
231257

232-
if (case_name_zv == NULL) {
258+
zend_object *case_obj = zend_enum_get_case_by_value(ce, long_key, string_key);
259+
// Updating of constants can fail
260+
if (EG(exception)) {
261+
RETURN_THROWS();
262+
}
263+
264+
if (case_obj == NULL) {
233265
if (try) {
234266
RETURN_NULL();
235267
}
@@ -243,19 +275,7 @@ static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
243275
RETURN_THROWS();
244276
}
245277

246-
// TODO: We might want to store pointers to constants in backed_enum_table instead of names,
247-
// to make this lookup more efficient.
248-
ZEND_ASSERT(Z_TYPE_P(case_name_zv) == IS_STRING);
249-
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), Z_STR_P(case_name_zv));
250-
ZEND_ASSERT(c != NULL);
251-
zval *case_zv = &c->value;
252-
if (Z_TYPE_P(case_zv) == IS_CONSTANT_AST) {
253-
if (zval_update_constant_ex(case_zv, c->ce) == FAILURE) {
254-
RETURN_THROWS();
255-
}
256-
}
257-
258-
ZVAL_COPY(return_value, case_zv);
278+
ZVAL_OBJ_COPY(return_value, case_obj);
259279
}
260280

261281
static ZEND_NAMED_FUNCTION(zend_enum_from_func)

Zend/zend_enum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, z
4040
ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value);
4141
ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name);
4242
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name);
43+
ZEND_API zend_object *zend_enum_get_case_by_value(zend_class_entry *ce, zend_long long_key, zend_string *string_key);
4344

4445
static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj)
4546
{

ext/zend_test/test.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ static zend_class_entry *zend_test_ns2_foo_class;
4545
static zend_class_entry *zend_test_ns2_ns_foo_class;
4646
static zend_class_entry *zend_test_unit_enum;
4747
static zend_class_entry *zend_test_string_enum;
48+
static zend_class_entry *zend_test_int_enum;
4849
static zend_object_handlers zend_test_class_handlers;
4950

5051
static ZEND_FUNCTION(zend_test_func)
@@ -588,6 +589,7 @@ PHP_MINIT_FUNCTION(zend_test)
588589

589590
zend_test_unit_enum = register_class_ZendTestUnitEnum();
590591
zend_test_string_enum = register_class_ZendTestStringEnum();
592+
zend_test_int_enum = register_class_ZendTestIntEnum();
591593

592594
// Loading via dl() not supported with the observer API
593595
if (type != MODULE_TEMPORARY) {

ext/zend_test/test.stub.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ enum ZendTestStringEnum: string {
6969
case Foo = "Test1";
7070
case Bar = "Test2";
7171
case Baz = "Test2\\a";
72+
case Qux = "42";
73+
}
74+
75+
enum ZendTestIntEnum: int {
76+
case Foo = 1;
77+
case Bar = 3;
78+
case Baz = -1;
7279
}
7380

7481
function zend_test_array_return(): array {}

ext/zend_test/test_arginfo.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 54adf746478423b7e7caa09d417fb97bb9621203 */
2+
* Stub hash: 49728df6f9ba6b1c8d3d350537b221c409fcde1b */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
55
ZEND_END_ARG_INFO()
@@ -236,6 +236,11 @@ static const zend_function_entry class_ZendTestStringEnum_methods[] = {
236236
};
237237

238238

239+
static const zend_function_entry class_ZendTestIntEnum_methods[] = {
240+
ZEND_FE_END
241+
};
242+
243+
239244
static const zend_function_entry class_ZendTestNS_Foo_methods[] = {
240245
ZEND_ME(ZendTestNS_Foo, method, arginfo_class_ZendTestNS_Foo_method, ZEND_ACC_PUBLIC)
241246
ZEND_FE_END
@@ -424,6 +429,30 @@ static zend_class_entry *register_class_ZendTestStringEnum(void)
424429
ZVAL_STR(&enum_case_Baz_value, enum_case_Baz_value_str);
425430
zend_enum_add_case_cstr(class_entry, "Baz", &enum_case_Baz_value);
426431

432+
zval enum_case_Qux_value;
433+
zend_string *enum_case_Qux_value_str = zend_string_init("42", sizeof("42") - 1, 1);
434+
ZVAL_STR(&enum_case_Qux_value, enum_case_Qux_value_str);
435+
zend_enum_add_case_cstr(class_entry, "Qux", &enum_case_Qux_value);
436+
437+
return class_entry;
438+
}
439+
440+
static zend_class_entry *register_class_ZendTestIntEnum(void)
441+
{
442+
zend_class_entry *class_entry = zend_register_internal_enum("ZendTestIntEnum", IS_LONG, class_ZendTestIntEnum_methods);
443+
444+
zval enum_case_Foo_value;
445+
ZVAL_LONG(&enum_case_Foo_value, 1);
446+
zend_enum_add_case_cstr(class_entry, "Foo", &enum_case_Foo_value);
447+
448+
zval enum_case_Bar_value;
449+
ZVAL_LONG(&enum_case_Bar_value, 3);
450+
zend_enum_add_case_cstr(class_entry, "Bar", &enum_case_Bar_value);
451+
452+
zval enum_case_Baz_value;
453+
ZVAL_LONG(&enum_case_Baz_value, -1);
454+
zend_enum_add_case_cstr(class_entry, "Baz", &enum_case_Baz_value);
455+
427456
return class_entry;
428457
}
429458

0 commit comments

Comments
 (0)