Skip to content

Commit ea87e84

Browse files
ReflectionClass: show enums differently from classes
While internally enums are mostly the same as classes, their output in `ReflectionClass::__toString()` should show the enum as the developer wrote it, rather than as the engine stored it. Accordingly - Say that the enum is an enum, not a final class - Include the backing type, if any, in the declaration line - List enum cases separately from constants, and show the underlying values, if any GH-15766
1 parent a098745 commit ea87e84

6 files changed

+135
-39
lines changed

ext/reflection/php_reflection.c

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c
306306
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent);
307307
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent);
308308
static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
309+
static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
309310
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent);
310311
static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent);
311312
static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent);
@@ -330,6 +331,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
330331
kind = "Interface";
331332
} else if (ce->ce_flags & ZEND_ACC_TRAIT) {
332333
kind = "Trait";
334+
} else if (ce->ce_flags & ZEND_ACC_ENUM) {
335+
kind = "Enum";
333336
}
334337
smart_str_append_printf(str, "%s%s [ ", indent, kind);
335338
}
@@ -345,6 +348,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
345348
smart_str_append_printf(str, "interface ");
346349
} else if (ce->ce_flags & ZEND_ACC_TRAIT) {
347350
smart_str_append_printf(str, "trait ");
351+
} else if (ce->ce_flags & ZEND_ACC_ENUM) {
352+
smart_str_append_printf(str, "enum ");
348353
} else {
349354
if (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
350355
smart_str_append_printf(str, "abstract ");
@@ -362,6 +367,14 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
362367
smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parent->name));
363368
}
364369

370+
if ((ce->ce_flags & ZEND_ACC_ENUM) && (ce->enum_backing_type != IS_UNDEF)) {
371+
// Show backing type
372+
if (ce->enum_backing_type == IS_STRING) {
373+
smart_str_append_printf(str, ": string");
374+
} else {
375+
smart_str_append_printf(str, ": int");
376+
}
377+
}
365378
if (ce->num_interfaces) {
366379
uint32_t i;
367380

@@ -384,23 +397,58 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
384397
}
385398

386399
/* Constants */
387-
smart_str_append_printf(str, "\n");
388-
count = zend_hash_num_elements(&ce->constants_table);
389-
smart_str_append_printf(str, "%s - Constants [%d] {\n", indent, count);
390-
if (count > 0) {
400+
int total_count = zend_hash_num_elements(&ce->constants_table);
401+
int constant_count = 0;
402+
int enum_case_count = 0;
403+
smart_str constant_str = {0};
404+
smart_str enum_case_str = {0};
405+
// So that we don't need to loop through all of the constants multiple
406+
// times (count the constants vs. enum cases, print the constants, print
407+
// the enum cases) use some temporary helper smart strings
408+
if (total_count > 0) {
391409
zend_string *key;
392410
zend_class_constant *c;
393411

394412
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
395-
_class_const_string(str, key, c, ZSTR_VAL(sub_indent));
413+
if (Z_TYPE(c->value) == IS_CONSTANT_AST
414+
&& zend_update_class_constant(c, key, c->ce) == FAILURE
415+
) {
416+
ZEND_ASSERT(EG(exception));
417+
zend_string_release(sub_indent);
418+
smart_str_free(&enum_case_str);
419+
smart_str_free(&constant_str);
420+
return;
421+
}
422+
if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE) {
423+
_enum_case_string(&enum_case_str, key, c, ZSTR_VAL(sub_indent));
424+
enum_case_count++;
425+
} else {
426+
_class_const_string(&constant_str, key, c, ZSTR_VAL(sub_indent));
427+
constant_count++;
428+
}
396429
if (UNEXPECTED(EG(exception))) {
397430
zend_string_release(sub_indent);
431+
smart_str_free(&enum_case_str);
432+
smart_str_free(&constant_str);
398433
return;
399434
}
400435
} ZEND_HASH_FOREACH_END();
401436
}
437+
// Enum cases go first, but the heading is only shown if there are any
438+
if (enum_case_count) {
439+
smart_str_append_printf(str, "\n");
440+
smart_str_append_printf(str, "%s - Enum cases [%d] {\n", indent, enum_case_count);
441+
smart_str_append_smart_str(str, &enum_case_str);
442+
smart_str_append_printf(str, "%s }\n", indent);
443+
}
444+
smart_str_append_printf(str, "\n");
445+
smart_str_append_printf(str, "%s - Constants [%d] {\n", indent, constant_count);
446+
smart_str_append_smart_str(str, &constant_str);
402447
smart_str_append_printf(str, "%s }\n", indent);
403448

449+
smart_str_free(&enum_case_str);
450+
smart_str_free(&constant_str);
451+
404452
/* Static properties */
405453
/* counting static properties */
406454
count = zend_hash_num_elements(&ce->properties_info);
@@ -626,6 +674,30 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl
626674
}
627675
/* }}} */
628676

677+
/* {{{ _enum_case_string */
678+
static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char *indent)
679+
{
680+
if (c->doc_comment) {
681+
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment));
682+
}
683+
smart_str_append_printf(str, "%sCase %s", indent, ZSTR_VAL(name));
684+
if (c->ce->enum_backing_type == IS_UNDEF) {
685+
// No value
686+
smart_str_appends(str, "\n");
687+
} else {
688+
// Has a value, which is the enum instance, get the value from that.
689+
// We know it must be either a string or integer so no need
690+
// for the IS_ARRAY or IS_OBJECT handling that _class_const_string()
691+
// requires
692+
zval *enum_val = zend_enum_fetch_case_value(Z_OBJ(c->value));
693+
zend_string *tmp_value_str;
694+
zend_string *value_str = zval_get_tmp_string(enum_val, &tmp_value_str);
695+
smart_str_append_printf(str, " = %s\n", ZSTR_VAL(value_str));
696+
zend_tmp_string_release(tmp_value_str);
697+
}
698+
}
699+
/* }}} */
700+
629701
static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset)
630702
{
631703
zend_op *op = op_array->opcodes;

ext/reflection/tests/ReflectionEnumUnitCase_getEnum.phpt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ echo (new ReflectionEnumUnitCase(Foo::class, 'Bar'))->getEnum();
1111

1212
?>
1313
--EXPECTF--
14-
Class [ <user> final class Foo implements UnitEnum ] {
14+
Enum [ <user> enum Foo implements UnitEnum ] {
1515
@@ %sReflectionEnumUnitCase_getEnum.php 3-5
1616

17-
- Constants [1] {
18-
Constant [ public Foo Bar ] { Object }
17+
- Enum cases [1] {
18+
Case Bar
19+
}
20+
21+
- Constants [0] {
1922
}
2023

2124
- Static properties [0] {

ext/reflection/tests/ReflectionEnum_toString.phpt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ echo new ReflectionEnum(Foo::class);
1111

1212
?>
1313
--EXPECTF--
14-
Class [ <user> final class Foo implements UnitEnum ] {
14+
Enum [ <user> enum Foo implements UnitEnum ] {
1515
@@ %sReflectionEnum_toString.php 3-5
1616

17-
- Constants [1] {
18-
Constant [ public Foo Bar ] { Object }
17+
- Enum cases [1] {
18+
Case Bar
19+
}
20+
21+
- Constants [0] {
1922
}
2023

2124
- Static properties [0] {

ext/reflection/tests/ReflectionEnum_toString_backed_int.phpt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ var_export( MyBool::cases() );
2828

2929
?>
3030
--EXPECTF--
31-
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
31+
Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
3232
@@ %sReflectionEnum_toString_backed_int.php 7-16
3333

34-
- Constants [3] {
35-
Constant [ public MyBool MyFalse ] { Object }
36-
Constant [ public MyBool MyTrue ] { Object }
34+
- Enum cases [2] {
35+
Case MyFalse = 0
36+
Case MyTrue = 1
37+
}
38+
39+
- Constants [1] {
3740
Constant [ public MyBool OtherTrue ] { Object }
3841
}
3942

@@ -81,12 +84,15 @@ Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum
8184
}
8285
}
8386

84-
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
87+
Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
8588
@@ %sReflectionEnum_toString_backed_int.php 7-16
8689

87-
- Constants [3] {
88-
Constant [ public MyBool MyFalse ] { Object }
89-
Constant [ public MyBool MyTrue ] { Object }
90+
- Enum cases [2] {
91+
Case MyFalse = 0
92+
Case MyTrue = 1
93+
}
94+
95+
- Constants [1] {
9096
Constant [ public MyBool OtherTrue ] { Object }
9197
}
9298

ext/reflection/tests/ReflectionEnum_toString_backed_string.phpt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ var_export( MyBool::cases() );
2828

2929
?>
3030
--EXPECTF--
31-
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
31+
Enum [ <user> enum MyBool: string implements MyStringable, UnitEnum, BackedEnum ] {
3232
@@ %sReflectionEnum_toString_backed_string.php 7-16
3333

34-
- Constants [3] {
35-
Constant [ public MyBool MyFalse ] { Object }
36-
Constant [ public MyBool MyTrue ] { Object }
34+
- Enum cases [2] {
35+
Case MyFalse = ~FALSE~
36+
Case MyTrue = ~TRUE~
37+
}
38+
39+
- Constants [1] {
3740
Constant [ public MyBool OtherTrue ] { Object }
3841
}
3942

@@ -81,12 +84,15 @@ Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum
8184
}
8285
}
8386

84-
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
87+
Enum [ <user> enum MyBool: string implements MyStringable, UnitEnum, BackedEnum ] {
8588
@@ %sReflectionEnum_toString_backed_string.php 7-16
8689

87-
- Constants [3] {
88-
Constant [ public MyBool MyFalse ] { Object }
89-
Constant [ public MyBool MyTrue ] { Object }
90+
- Enum cases [2] {
91+
Case MyFalse = ~FALSE~
92+
Case MyTrue = ~TRUE~
93+
}
94+
95+
- Constants [1] {
9096
Constant [ public MyBool OtherTrue ] { Object }
9197
}
9298

ext/reflection/tests/ReflectionEnum_toString_unbacked.phpt

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ var_export( Suit::cases() );
3030

3131
?>
3232
--EXPECTF--
33-
Class [ <user> final class Suit implements MyStringable, UnitEnum ] {
33+
Enum [ <user> enum Suit implements MyStringable, UnitEnum ] {
3434
@@ %sReflectionEnum_toString_unbacked.php 7-18
3535

36-
- Constants [5] {
37-
Constant [ public Suit Hearts ] { Object }
38-
Constant [ public Suit Diamonds ] { Object }
39-
Constant [ public Suit Clubs ] { Object }
40-
Constant [ public Suit Spades ] { Object }
36+
- Enum cases [4] {
37+
Case Hearts
38+
Case Diamonds
39+
Case Clubs
40+
Case Spades
41+
}
42+
43+
- Constants [1] {
4144
Constant [ public Suit OtherHearts ] { Object }
4245
}
4346

@@ -68,14 +71,17 @@ Class [ <user> final class Suit implements MyStringable, UnitEnum ] {
6871
}
6972
}
7073

71-
Class [ <user> final class Suit implements MyStringable, UnitEnum ] {
74+
Enum [ <user> enum Suit implements MyStringable, UnitEnum ] {
7275
@@ %sReflectionEnum_toString_unbacked.php 7-18
7376

74-
- Constants [5] {
75-
Constant [ public Suit Hearts ] { Object }
76-
Constant [ public Suit Diamonds ] { Object }
77-
Constant [ public Suit Clubs ] { Object }
78-
Constant [ public Suit Spades ] { Object }
77+
- Enum cases [4] {
78+
Case Hearts
79+
Case Diamonds
80+
Case Clubs
81+
Case Spades
82+
}
83+
84+
- Constants [1] {
7985
Constant [ public Suit OtherHearts ] { Object }
8086
}
8187

0 commit comments

Comments
 (0)