mirror of
https://github.com/php/php-src.git
synced 2026-03-24 08:12:21 +01:00
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
This commit is contained in:
committed by
DanielEScherzer
parent
6c81f708c5
commit
4233394e8f
@@ -306,6 +306,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c
|
||||
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent);
|
||||
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent);
|
||||
static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
|
||||
static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
|
||||
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent);
|
||||
static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent);
|
||||
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
|
||||
kind = "Interface";
|
||||
} else if (ce->ce_flags & ZEND_ACC_TRAIT) {
|
||||
kind = "Trait";
|
||||
} else if (ce->ce_flags & ZEND_ACC_ENUM) {
|
||||
kind = "Enum";
|
||||
}
|
||||
smart_str_append_printf(str, "%s%s [ ", indent, kind);
|
||||
}
|
||||
@@ -345,6 +348,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
|
||||
smart_str_append_printf(str, "interface ");
|
||||
} else if (ce->ce_flags & ZEND_ACC_TRAIT) {
|
||||
smart_str_append_printf(str, "trait ");
|
||||
} else if (ce->ce_flags & ZEND_ACC_ENUM) {
|
||||
smart_str_append_printf(str, "enum ");
|
||||
} else {
|
||||
if (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
|
||||
smart_str_append_printf(str, "abstract ");
|
||||
@@ -362,6 +367,12 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
|
||||
smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parent->name));
|
||||
}
|
||||
|
||||
// Show backing type of enums
|
||||
if ((ce->ce_flags & ZEND_ACC_ENUM) && (ce->enum_backing_type != IS_UNDEF)) {
|
||||
smart_str_append_printf(str,
|
||||
ce->enum_backing_type == IS_STRING ? ": string" : ": int"
|
||||
);
|
||||
}
|
||||
if (ce->num_interfaces) {
|
||||
uint32_t i;
|
||||
|
||||
@@ -384,23 +395,49 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
|
||||
}
|
||||
|
||||
/* Constants */
|
||||
smart_str_append_printf(str, "\n");
|
||||
count = zend_hash_num_elements(&ce->constants_table);
|
||||
smart_str_append_printf(str, "%s - Constants [%d] {\n", indent, count);
|
||||
if (count > 0) {
|
||||
uint32_t total_count = zend_hash_num_elements(&ce->constants_table);
|
||||
uint32_t constant_count = 0;
|
||||
uint32_t enum_case_count = 0;
|
||||
smart_str constant_str = {0};
|
||||
smart_str enum_case_str = {0};
|
||||
/* So that we don't need to loop through all of the constants multiple
|
||||
* times (count the constants vs. enum cases, print the constants, print
|
||||
* the enum cases) use some temporary helper smart strings. */
|
||||
if (total_count > 0) {
|
||||
zend_string *key;
|
||||
zend_class_constant *c;
|
||||
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
|
||||
_class_const_string(str, key, c, ZSTR_VAL(sub_indent));
|
||||
if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE) {
|
||||
_enum_case_string(&enum_case_str, key, c, ZSTR_VAL(sub_indent));
|
||||
enum_case_count++;
|
||||
} else {
|
||||
_class_const_string(&constant_str, key, c, ZSTR_VAL(sub_indent));
|
||||
constant_count++;
|
||||
}
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
zend_string_release(sub_indent);
|
||||
smart_str_free(&enum_case_str);
|
||||
smart_str_free(&constant_str);
|
||||
return;
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
// Enum cases go first, but the heading is only shown if there are any
|
||||
if (enum_case_count) {
|
||||
smart_str_appendc(str, '\n');
|
||||
smart_str_append_printf(str, "%s - Enum cases [%d] {\n", indent, enum_case_count);
|
||||
smart_str_append_smart_str(str, &enum_case_str);
|
||||
smart_str_append_printf(str, "%s }\n", indent);
|
||||
}
|
||||
smart_str_appendc(str, '\n');
|
||||
smart_str_append_printf(str, "%s - Constants [%d] {\n", indent, constant_count);
|
||||
smart_str_append_smart_str(str, &constant_str);
|
||||
smart_str_append_printf(str, "%s }\n", indent);
|
||||
|
||||
smart_str_free(&enum_case_str);
|
||||
smart_str_free(&constant_str);
|
||||
|
||||
/* Static properties */
|
||||
/* counting static properties */
|
||||
count = zend_hash_num_elements(&ce->properties_info);
|
||||
@@ -626,6 +663,32 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char *indent)
|
||||
{
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST && zend_update_class_constant(c, name, c->ce) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->doc_comment) {
|
||||
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment));
|
||||
}
|
||||
smart_str_append_printf(str, "%sCase %s", indent, ZSTR_VAL(name));
|
||||
if (c->ce->enum_backing_type == IS_UNDEF) {
|
||||
// No value
|
||||
smart_str_appendc(str, '\n');
|
||||
} else {
|
||||
/* Has a value, which is the enum instance, get the value from that.
|
||||
* We know it must be either a string or integer so no need
|
||||
* for the IS_ARRAY or IS_OBJECT handling that _class_const_string()
|
||||
* requires. */
|
||||
zval *enum_val = zend_enum_fetch_case_value(Z_OBJ(c->value));
|
||||
zend_string *tmp_value_str;
|
||||
zend_string *value_str = zval_get_tmp_string(enum_val, &tmp_value_str);
|
||||
smart_str_append_printf(str, " = %s\n", ZSTR_VAL(value_str));
|
||||
zend_tmp_string_release(tmp_value_str);
|
||||
}
|
||||
}
|
||||
|
||||
static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset)
|
||||
{
|
||||
zend_op *op = op_array->opcodes;
|
||||
|
||||
@@ -11,11 +11,14 @@ echo (new ReflectionEnumUnitCase(Foo::class, 'Bar'))->getEnum();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Class [ <user> final class Foo implements UnitEnum ] {
|
||||
Enum [ <user> enum Foo implements UnitEnum ] {
|
||||
@@ %sReflectionEnumUnitCase_getEnum.php 3-5
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public Foo Bar ] { Object }
|
||||
- Enum cases [1] {
|
||||
Case Bar
|
||||
}
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
|
||||
@@ -11,11 +11,14 @@ echo new ReflectionEnum(Foo::class);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Class [ <user> final class Foo implements UnitEnum ] {
|
||||
Enum [ <user> enum Foo implements UnitEnum ] {
|
||||
@@ %sReflectionEnum_toString.php 3-5
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public Foo Bar ] { Object }
|
||||
- Enum cases [1] {
|
||||
Case Bar
|
||||
}
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
|
||||
@@ -28,12 +28,15 @@ var_export( MyBool::cases() );
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
@@ %sReflectionEnum_toString_backed_int.php 7-16
|
||||
|
||||
- Constants [3] {
|
||||
Constant [ public MyBool MyFalse ] { Object }
|
||||
Constant [ public MyBool MyTrue ] { Object }
|
||||
- Enum cases [2] {
|
||||
Case MyFalse = 0
|
||||
Case MyTrue = 1
|
||||
}
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public MyBool OtherTrue ] { Object }
|
||||
}
|
||||
|
||||
@@ -81,12 +84,15 @@ Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum
|
||||
}
|
||||
}
|
||||
|
||||
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
@@ %sReflectionEnum_toString_backed_int.php 7-16
|
||||
|
||||
- Constants [3] {
|
||||
Constant [ public MyBool MyFalse ] { Object }
|
||||
Constant [ public MyBool MyTrue ] { Object }
|
||||
- Enum cases [2] {
|
||||
Case MyFalse = 0
|
||||
Case MyTrue = 1
|
||||
}
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public MyBool OtherTrue ] { Object }
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,15 @@ var_export( MyBool::cases() );
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
Enum [ <user> enum MyBool: string implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
@@ %sReflectionEnum_toString_backed_string.php 7-16
|
||||
|
||||
- Constants [3] {
|
||||
Constant [ public MyBool MyFalse ] { Object }
|
||||
Constant [ public MyBool MyTrue ] { Object }
|
||||
- Enum cases [2] {
|
||||
Case MyFalse = ~FALSE~
|
||||
Case MyTrue = ~TRUE~
|
||||
}
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public MyBool OtherTrue ] { Object }
|
||||
}
|
||||
|
||||
@@ -81,12 +84,15 @@ Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum
|
||||
}
|
||||
}
|
||||
|
||||
Class [ <user> final class MyBool implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
Enum [ <user> enum MyBool: string implements MyStringable, UnitEnum, BackedEnum ] {
|
||||
@@ %sReflectionEnum_toString_backed_string.php 7-16
|
||||
|
||||
- Constants [3] {
|
||||
Constant [ public MyBool MyFalse ] { Object }
|
||||
Constant [ public MyBool MyTrue ] { Object }
|
||||
- Enum cases [2] {
|
||||
Case MyFalse = ~FALSE~
|
||||
Case MyTrue = ~TRUE~
|
||||
}
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public MyBool OtherTrue ] { Object }
|
||||
}
|
||||
|
||||
|
||||
@@ -30,14 +30,17 @@ var_export( Suit::cases() );
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Class [ <user> final class Suit implements MyStringable, UnitEnum ] {
|
||||
Enum [ <user> enum Suit implements MyStringable, UnitEnum ] {
|
||||
@@ %sReflectionEnum_toString_unbacked.php 7-18
|
||||
|
||||
- Constants [5] {
|
||||
Constant [ public Suit Hearts ] { Object }
|
||||
Constant [ public Suit Diamonds ] { Object }
|
||||
Constant [ public Suit Clubs ] { Object }
|
||||
Constant [ public Suit Spades ] { Object }
|
||||
- Enum cases [4] {
|
||||
Case Hearts
|
||||
Case Diamonds
|
||||
Case Clubs
|
||||
Case Spades
|
||||
}
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public Suit OtherHearts ] { Object }
|
||||
}
|
||||
|
||||
@@ -68,14 +71,17 @@ Class [ <user> final class Suit implements MyStringable, UnitEnum ] {
|
||||
}
|
||||
}
|
||||
|
||||
Class [ <user> final class Suit implements MyStringable, UnitEnum ] {
|
||||
Enum [ <user> enum Suit implements MyStringable, UnitEnum ] {
|
||||
@@ %sReflectionEnum_toString_unbacked.php 7-18
|
||||
|
||||
- Constants [5] {
|
||||
Constant [ public Suit Hearts ] { Object }
|
||||
Constant [ public Suit Diamonds ] { Object }
|
||||
Constant [ public Suit Clubs ] { Object }
|
||||
Constant [ public Suit Spades ] { Object }
|
||||
- Enum cases [4] {
|
||||
Case Hearts
|
||||
Case Diamonds
|
||||
Case Clubs
|
||||
Case Spades
|
||||
}
|
||||
|
||||
- Constants [1] {
|
||||
Constant [ public Suit OtherHearts ] { Object }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user