diff options
author | Ilija Tovilo <ilija.tovilo@me.com> | 2020-06-10 23:10:18 +0200 |
---|---|---|
committer | Ilija Tovilo <ilija.tovilo@me.com> | 2021-03-17 19:08:03 +0100 |
commit | 269c8dac1d56ee85d71ae94d9b28dd7d8e8de7b7 (patch) | |
tree | 810ac41b2157ff4e8063f9696f97e1a9d77837c4 /ext/reflection/php_reflection.c | |
parent | a6fc427b8c51015c16541c112a26dd06bd75e99e (diff) | |
download | php-git-269c8dac1d56ee85d71ae94d9b28dd7d8e8de7b7.tar.gz |
Implement enums
RFC: https://wiki.php.net/rfc/enumerations
Co-authored-by: Nikita Popov <nikita.ppv@gmail.com>
Closes GH-6489.
Diffstat (limited to 'ext/reflection/php_reflection.c')
-rw-r--r-- | ext/reflection/php_reflection.c | 255 |
1 files changed, 252 insertions, 3 deletions
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 83d165beb2..280b3fc06c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -43,6 +43,7 @@ #include "zend_extensions.h" #include "zend_builtin_functions.h" #include "zend_smart_str.h" +#include "zend_enum.h" #include "php_reflection_arginfo.h" /* Key used to avoid leaking addresses in ReflectionProperty::getId() */ @@ -87,6 +88,9 @@ PHPAPI zend_class_entry *reflection_extension_ptr; PHPAPI zend_class_entry *reflection_zend_extension_ptr; PHPAPI zend_class_entry *reflection_reference_ptr; PHPAPI zend_class_entry *reflection_attribute_ptr; +PHPAPI zend_class_entry *reflection_enum_ptr; +PHPAPI zend_class_entry *reflection_enum_unit_case_ptr; +PHPAPI zend_class_entry *reflection_enum_backed_case_ptr; /* Exception throwing macro */ #define _DO_THROW(msg) \ @@ -573,6 +577,9 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant if (Z_TYPE(c->value) == IS_ARRAY) { smart_str_append_printf(str, "%sConstant [ %s %s %s ] { Array }\n", indent, visibility, type, name); + } else if (Z_TYPE(c->value) == IS_OBJECT) { + smart_str_append_printf(str, "%sConstant [ %s %s %s ] { Object }\n", + indent, visibility, type, name); } else { zend_string *tmp_value_str; zend_string *value_str = zval_get_tmp_string(&c->value, &tmp_value_str); @@ -1251,6 +1258,18 @@ PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object) } /* }}} */ +static void zend_reflection_enum_factory(zend_class_entry *ce, zval *object) +{ + reflection_object *intern; + + reflection_instantiate(reflection_enum_ptr, object); + intern = Z_REFLECTION_P(object); + intern->ptr = ce; + intern->ref_type = REF_TYPE_OTHER; + intern->ce = ce; + ZVAL_STR_COPY(reflection_prop_name(object), ce->name); +} + /* {{{ reflection_extension_factory */ static void reflection_extension_factory(zval *object, const char *name_str) { @@ -1430,6 +1449,24 @@ static void reflection_class_constant_factory(zend_string *name_str, zend_class_ } /* }}} */ +static void reflection_enum_case_factory(zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object) +{ + reflection_object *intern; + + zend_class_entry *case_reflection_class = ce->backed_enum_table == IS_UNDEF + ? reflection_enum_unit_case_ptr + : reflection_enum_backed_case_ptr; + reflection_instantiate(case_reflection_class, object); + intern = Z_REFLECTION_P(object); + intern->ptr = constant; + intern->ref_type = REF_TYPE_CLASS_CONSTANT; + intern->ce = constant->ce; + intern->ignore_visibility = 0; + + ZVAL_STR_COPY(reflection_prop_name(object), name_str); + ZVAL_STR_COPY(reflection_prop_class(object), constant->ce->name); +} + static int get_parameter_default(zval *result, parameter_reference *param) { if (param->fptr->type == ZEND_INTERNAL_FUNCTION) { if (param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO) { @@ -3665,10 +3702,10 @@ ZEND_METHOD(ReflectionClassConstant, getValue) } GET_REFLECTION_OBJECT_PTR(ref); - ZVAL_COPY_OR_DUP(return_value, &ref->value); - if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) { - zval_update_constant_ex(return_value, ref->ce); + if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { + zval_update_constant_ex(&ref->value, ref->ce); } + ZVAL_COPY_OR_DUP(return_value, &ref->value); } /* }}} */ @@ -3718,6 +3755,16 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes) } /* }}} */ +ZEND_METHOD(ReflectionClassConstant, isEnumCase) +{ + reflection_object *intern; + zend_class_constant *ref; + + GET_REFLECTION_OBJECT_PTR(ref); + + RETURN_BOOL(Z_ACCESS_FLAGS(ref->value) & ZEND_CLASS_CONST_IS_CASE); +} + /* {{{ reflection_class_object_ctor */ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_object) { @@ -4637,6 +4684,11 @@ ZEND_METHOD(ReflectionClass, isTrait) } /* }}} */ +ZEND_METHOD(ReflectionClass, isEnum) +{ + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_ENUM); +} + /* {{{ Returns whether this class is final */ ZEND_METHOD(ReflectionClass, isFinal) { @@ -6494,6 +6546,194 @@ ZEND_METHOD(ReflectionAttribute, newInstance) RETURN_COPY_VALUE(&obj); } +ZEND_METHOD(ReflectionEnum, __construct) +{ + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + if (EG(exception)) { + RETURN_THROWS(); + } + + reflection_object *intern; + zend_class_entry *ce; + GET_REFLECTION_OBJECT_PTR(ce); + + if (!(ce->ce_flags & ZEND_ACC_ENUM)) { + zend_throw_exception_ex(reflection_exception_ptr, -1, "Class \"%s\" is not an enum", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } +} + +ZEND_METHOD(ReflectionEnum, hasCase) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_string *name; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ce); + + zend_class_constant *class_const = zend_hash_find_ptr(&ce->constants_table, name); + if (class_const == NULL) { + RETURN_FALSE; + } + + RETURN_BOOL(Z_ACCESS_FLAGS(class_const->value) & ZEND_CLASS_CONST_IS_CASE); +} + +ZEND_METHOD(ReflectionEnum, getCase) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_string *name; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ce); + + zend_class_constant *constant = zend_hash_find_ptr(&ce->constants_table, name); + if (constant == NULL) { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Case %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name)); + RETURN_THROWS(); + } + if (!(Z_ACCESS_FLAGS(constant->value) & ZEND_CLASS_CONST_IS_CASE)) { + zend_throw_exception_ex(reflection_exception_ptr, 0, "%s::%s is not a case", ZSTR_VAL(ce->name), ZSTR_VAL(name)); + RETURN_THROWS(); + } + + reflection_enum_case_factory(ce, name, constant, return_value); +} + +ZEND_METHOD(ReflectionEnum, getCases) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_string *name; + zend_class_constant *constant; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ce); + + array_init(return_value); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) { + if (Z_ACCESS_FLAGS(constant->value) & ZEND_CLASS_CONST_IS_CASE) { + zval class_const; + reflection_enum_case_factory(ce, name, constant, &class_const); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &class_const); + } + } ZEND_HASH_FOREACH_END(); +} + +ZEND_METHOD(ReflectionEnum, isBacked) +{ + reflection_object *intern; + zend_class_entry *ce; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ce); + RETURN_BOOL(ce->enum_backing_type != IS_UNDEF); +} + +ZEND_METHOD(ReflectionEnum, getBackingType) +{ + reflection_object *intern; + zend_class_entry *ce; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ce); + + if (ce->enum_backing_type == IS_UNDEF) { + RETURN_NULL(); + } else { + zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0); + reflection_type_factory(type, return_value, 0); + } +} + +ZEND_METHOD(ReflectionEnumUnitCase, __construct) +{ + ZEND_MN(ReflectionClassConstant___construct)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (EG(exception)) { + RETURN_THROWS(); + } + + reflection_object *intern; + zend_class_constant *ref; + + GET_REFLECTION_OBJECT_PTR(ref); + + if (!(Z_ACCESS_FLAGS(ref->value) & ZEND_CLASS_CONST_IS_CASE)) { + zval *case_name = reflection_prop_name(ZEND_THIS); + zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s is not a case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name)); + RETURN_THROWS(); + } +} + +ZEND_METHOD(ReflectionEnumUnitCase, getEnum) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(ref); + + zend_reflection_enum_factory(ref->ce, return_value); +} + +ZEND_METHOD(ReflectionEnumBackedCase, __construct) +{ + ZEND_MN(ReflectionEnumUnitCase___construct)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (EG(exception)) { + RETURN_THROWS(); + } + + reflection_object *intern; + zend_class_constant *ref; + + GET_REFLECTION_OBJECT_PTR(ref); + + if (ref->ce->enum_backing_type == IS_UNDEF) { + zval *case_name = reflection_prop_name(ZEND_THIS); + zend_throw_exception_ex(reflection_exception_ptr, 0, "Enum case %s::%s is not a backed case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name)); + RETURN_THROWS(); + } +} + +ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(ref); + + if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { + zval_update_constant_ex(&ref->value, ref->ce); + } + + ZEND_ASSERT(intern->ce->enum_backing_type != IS_UNDEF); + zval *member_p = zend_enum_fetch_case_value(Z_OBJ(ref->value)); + + ZVAL_COPY_OR_DUP(return_value, member_p); +} + /* {{{ _reflection_write_property */ static zval *_reflection_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { @@ -6603,6 +6843,15 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_attribute_ptr = register_class_ReflectionAttribute(); reflection_init_class_handlers(reflection_attribute_ptr); + reflection_enum_ptr = register_class_ReflectionEnum(reflection_class_ptr); + reflection_init_class_handlers(reflection_enum_ptr); + + reflection_enum_unit_case_ptr = register_class_ReflectionEnumUnitCase(reflection_class_constant_ptr); + reflection_init_class_handlers(reflection_enum_unit_case_ptr); + + reflection_enum_backed_case_ptr = register_class_ReflectionEnumBackedCase(reflection_enum_unit_case_ptr); + reflection_init_class_handlers(reflection_enum_backed_case_ptr); + REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "IS_INSTANCEOF", REFLECTION_ATTRIBUTE_IS_INSTANCEOF); REFLECTION_G(key_initialized) = 0; |