diff options
Diffstat (limited to 'ext/reflection')
21 files changed, 876 insertions, 13 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; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index 654ba55256..de368a86c0 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -43,6 +43,9 @@ extern PHPAPI zend_class_entry *reflection_extension_ptr; extern PHPAPI zend_class_entry *reflection_zend_extension_ptr; extern PHPAPI zend_class_entry *reflection_reference_ptr; extern PHPAPI zend_class_entry *reflection_attribute_ptr; +extern PHPAPI zend_class_entry *reflection_enum_ptr; +extern PHPAPI zend_class_entry *reflection_enum_unit_case_ptr; +extern PHPAPI zend_class_entry *reflection_enum_backed_case_ptr; PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 9287ae7e9c..e9815558d6 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -298,6 +298,8 @@ class ReflectionClass implements Reflector /** @return bool */ public function isTrait() {} + public function isEnum(): bool {} + /** @return bool */ public function isAbstract() {} @@ -479,6 +481,8 @@ class ReflectionClassConstant implements Reflector /** @return ReflectionAttribute[] */ public function getAttributes(?string $name = null, int $flags = 0): array {} + + public function isEnumCase(): bool {} } class ReflectionParameter implements Reflector @@ -683,3 +687,36 @@ final class ReflectionAttribute private function __construct() {} } + +final class ReflectionEnum extends ReflectionClass +{ + public function __construct(object|string $objectOrClass) {} + + public function hasCase(string $name): bool {} + + public function getCase(string $name): ReflectionEnumUnitCase {} + + /** @return ReflectionEnumUnitCase[] */ + public function getCases(): array {} + + public function isBacked(): bool {} + + public function getBackingType(): ReflectionType|null {} +} + +class ReflectionEnumUnitCase extends ReflectionClassConstant +{ + public function __construct(object|string $class, string $constant) {} + + public function getEnum(): ReflectionEnum {} + + /** @implementation-alias ReflectionClassConstant::getValue */ + public function getValue(): UnitEnum {} +} + +final class ReflectionEnumBackedCase extends ReflectionEnumUnitCase +{ + public function __construct(object|string $class, string $constant) {} + + public function getBackingValue(): int|string {} +} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index a9df516fc2..36e6dc3cec 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fb4e29d088862cc76d22c9902c79c86dbfa7ac95 */ + * Stub hash: 3594ec0b0c3ed7266223be9c6b426aac56e3aabe */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -218,6 +218,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_isTrait arginfo_class_ReflectionFunctionAbstract_inNamespace +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_isEnum, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_ReflectionClass_isAbstract arginfo_class_ReflectionFunctionAbstract_inNamespace #define arginfo_class_ReflectionClass_isFinal arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -308,8 +311,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_isDefault arginfo_class_ReflectionFunctionAbstract_inNamespace -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_isPromoted, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionClass_isEnum #define arginfo_class_ReflectionProperty_getModifiers arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -323,7 +325,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_hasType arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionProperty_isPromoted +#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionClass_isEnum #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -356,6 +358,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes +#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionClass_isEnum + #define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2) @@ -401,7 +405,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionProperty_isPromoted +#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionClass_isEnum #define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes @@ -478,7 +482,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_getTarget, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionProperty_isPromoted +#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionClass_isEnum #define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes @@ -489,6 +493,36 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionAttribute___construct arginfo_class_ReflectionFunctionAbstract_inNamespace +#define arginfo_class_ReflectionEnum___construct arginfo_class_ReflectionClass___construct + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionEnum_hasCase, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnum_getCase, 0, 1, ReflectionEnumUnitCase, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionEnum_getCases arginfo_class_ReflectionUnionType_getTypes + +#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionClass_isEnum + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnum_getBackingType, 0, 0, ReflectionType, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionEnumUnitCase___construct arginfo_class_ReflectionClassConstant___construct + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnumUnitCase_getEnum, 0, 0, ReflectionEnum, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnumUnitCase_getValue, 0, 0, UnitEnum, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionEnumBackedCase___construct arginfo_class_ReflectionClassConstant___construct + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ReflectionEnumBackedCase_getBackingValue, 0, 0, MAY_BE_LONG|MAY_BE_STRING) +ZEND_END_ARG_INFO() + ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); @@ -579,6 +613,7 @@ ZEND_METHOD(ReflectionClass, getTraits); ZEND_METHOD(ReflectionClass, getTraitNames); ZEND_METHOD(ReflectionClass, getTraitAliases); ZEND_METHOD(ReflectionClass, isTrait); +ZEND_METHOD(ReflectionClass, isEnum); ZEND_METHOD(ReflectionClass, isAbstract); ZEND_METHOD(ReflectionClass, isFinal); ZEND_METHOD(ReflectionClass, getModifiers); @@ -633,6 +668,7 @@ ZEND_METHOD(ReflectionClassConstant, getModifiers); ZEND_METHOD(ReflectionClassConstant, getDeclaringClass); ZEND_METHOD(ReflectionClassConstant, getDocComment); ZEND_METHOD(ReflectionClassConstant, getAttributes); +ZEND_METHOD(ReflectionClassConstant, isEnumCase); ZEND_METHOD(ReflectionParameter, __construct); ZEND_METHOD(ReflectionParameter, __toString); ZEND_METHOD(ReflectionParameter, getName); @@ -690,6 +726,16 @@ ZEND_METHOD(ReflectionAttribute, getArguments); ZEND_METHOD(ReflectionAttribute, newInstance); ZEND_METHOD(ReflectionAttribute, __clone); ZEND_METHOD(ReflectionAttribute, __construct); +ZEND_METHOD(ReflectionEnum, __construct); +ZEND_METHOD(ReflectionEnum, hasCase); +ZEND_METHOD(ReflectionEnum, getCase); +ZEND_METHOD(ReflectionEnum, getCases); +ZEND_METHOD(ReflectionEnum, isBacked); +ZEND_METHOD(ReflectionEnum, getBackingType); +ZEND_METHOD(ReflectionEnumUnitCase, __construct); +ZEND_METHOD(ReflectionEnumUnitCase, getEnum); +ZEND_METHOD(ReflectionEnumBackedCase, __construct); +ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue); static const zend_function_entry class_ReflectionException_methods[] = { @@ -818,6 +864,7 @@ static const zend_function_entry class_ReflectionClass_methods[] = { ZEND_ME(ReflectionClass, getTraitNames, arginfo_class_ReflectionClass_getTraitNames, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getTraitAliases, arginfo_class_ReflectionClass_getTraitAliases, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isTrait, arginfo_class_ReflectionClass_isTrait, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, isEnum, arginfo_class_ReflectionClass_isEnum, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isAbstract, arginfo_class_ReflectionClass_isAbstract, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isFinal, arginfo_class_ReflectionClass_isFinal, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getModifiers, arginfo_class_ReflectionClass_getModifiers, ZEND_ACC_PUBLIC) @@ -890,6 +937,7 @@ static const zend_function_entry class_ReflectionClassConstant_methods[] = { ZEND_ME(ReflectionClassConstant, getDeclaringClass, arginfo_class_ReflectionClassConstant_getDeclaringClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getAttributes, arginfo_class_ReflectionClassConstant_getAttributes, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassConstant, isEnumCase, arginfo_class_ReflectionClassConstant_isEnumCase, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -995,6 +1043,32 @@ static const zend_function_entry class_ReflectionAttribute_methods[] = { ZEND_FE_END }; + +static const zend_function_entry class_ReflectionEnum_methods[] = { + ZEND_ME(ReflectionEnum, __construct, arginfo_class_ReflectionEnum___construct, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnum, hasCase, arginfo_class_ReflectionEnum_hasCase, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnum, getCase, arginfo_class_ReflectionEnum_getCase, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnum, getCases, arginfo_class_ReflectionEnum_getCases, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnum, isBacked, arginfo_class_ReflectionEnum_isBacked, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnum, getBackingType, arginfo_class_ReflectionEnum_getBackingType, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +static const zend_function_entry class_ReflectionEnumUnitCase_methods[] = { + ZEND_ME(ReflectionEnumUnitCase, __construct, arginfo_class_ReflectionEnumUnitCase___construct, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnumUnitCase, getEnum, arginfo_class_ReflectionEnumUnitCase_getEnum, ZEND_ACC_PUBLIC) + ZEND_MALIAS(ReflectionClassConstant, getValue, getValue, arginfo_class_ReflectionEnumUnitCase_getValue, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +static const zend_function_entry class_ReflectionEnumBackedCase_methods[] = { + ZEND_ME(ReflectionEnumBackedCase, __construct, arginfo_class_ReflectionEnumBackedCase___construct, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionEnumBackedCase, getBackingValue, arginfo_class_ReflectionEnumBackedCase_getBackingValue, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_ReflectionException(zend_class_entry *class_entry_Exception) { zend_class_entry ce, *class_entry; @@ -1258,3 +1332,35 @@ static zend_class_entry *register_class_ReflectionAttribute(void) return class_entry; } + +static zend_class_entry *register_class_ReflectionEnum(zend_class_entry *class_entry_ReflectionClass) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionEnum", class_ReflectionEnum_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ReflectionClass); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} + +static zend_class_entry *register_class_ReflectionEnumUnitCase(zend_class_entry *class_entry_ReflectionClassConstant) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionEnumUnitCase", class_ReflectionEnumUnitCase_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ReflectionClassConstant); + + return class_entry; +} + +static zend_class_entry *register_class_ReflectionEnumBackedCase(zend_class_entry *class_entry_ReflectionEnumUnitCase) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionEnumBackedCase", class_ReflectionEnumBackedCase_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ReflectionEnumUnitCase); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + return class_entry; +} diff --git a/ext/reflection/tests/ReflectionClassConstant_isEnumCase.phpt b/ext/reflection/tests/ReflectionClassConstant_isEnumCase.phpt new file mode 100644 index 0000000000..7125075085 --- /dev/null +++ b/ext/reflection/tests/ReflectionClassConstant_isEnumCase.phpt @@ -0,0 +1,23 @@ +--TEST-- +ReflectionClassConstant::isEnumCase() +--FILE-- +<?php + +enum Foo { + case Bar; + const Baz = self::Bar; +} + +class Qux { + const Quux = 0; +} + +var_dump((new ReflectionClassConstant(Foo::class, 'Bar'))->isEnumCase()); +var_dump((new ReflectionClassConstant(Foo::class, 'Baz'))->isEnumCase()); +var_dump((new ReflectionClassConstant(Qux::class, 'Quux'))->isEnumCase()); + +?> +--EXPECT-- +bool(true) +bool(false) +bool(false) diff --git a/ext/reflection/tests/ReflectionClass_isEnum.phpt b/ext/reflection/tests/ReflectionClass_isEnum.phpt new file mode 100644 index 0000000000..b781c61509 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_isEnum.phpt @@ -0,0 +1,20 @@ +--TEST-- +Testing ReflectionClass::isEnum() +--FILE-- +<?php + +class Foo {} +enum Bar { + case Baz; +} + +$fooReflection = new ReflectionClass(Foo::class); +$barReflection = new ReflectionClass(Bar::class); + +var_dump($fooReflection->isEnum()); +var_dump($barReflection->isEnum()); + +?> +--EXPECT-- +bool(false) +bool(true) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index e218e8b470..e8baf72689 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -27,7 +27,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector, String Property [ public string $name ] } - - Methods [54] { + - Methods [55] { Method [ <internal:Reflection> final private method __clone ] { - Parameters [0] { @@ -234,6 +234,13 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector, String } } + Method [ <internal:Reflection> public method isEnum ] { + + - Parameters [0] { + } + - Return [ bool ] + } + Method [ <internal:Reflection> public method isAbstract ] { - Parameters [0] { diff --git a/ext/reflection/tests/ReflectionEnumBackedCase_getBackingValue.phpt b/ext/reflection/tests/ReflectionEnumBackedCase_getBackingValue.phpt new file mode 100644 index 0000000000..f81241d73a --- /dev/null +++ b/ext/reflection/tests/ReflectionEnumBackedCase_getBackingValue.phpt @@ -0,0 +1,38 @@ +--TEST-- +ReflectionEnumBackedCase::getBackingValue() +--FILE-- +<?php + +enum Enum_ { + case Foo; +} + +enum IntEnum: int { + case Foo = 0; +} + +enum StringEnum: string { + case Foo = 'Foo'; +} + +try { + var_dump(new ReflectionEnumBackedCase(Enum_::class, 'Foo')); +} catch (ReflectionException $e) { + echo $e->getMessage() . "\n"; +} + +try { + var_dump(new ReflectionEnumBackedCase([], 'Foo')); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +var_dump((new ReflectionEnumBackedCase(IntEnum::class, 'Foo'))->getBackingValue()); +var_dump((new ReflectionEnumBackedCase(StringEnum::class, 'Foo'))->getBackingValue()); + +?> +--EXPECT-- +Enum case Enum_::Foo is not a backed case +ReflectionEnumBackedCase::__construct(): Argument #1 ($class) must be of type object|string, array given +int(0) +string(3) "Foo" diff --git a/ext/reflection/tests/ReflectionEnumUnitCase_construct.phpt b/ext/reflection/tests/ReflectionEnumUnitCase_construct.phpt new file mode 100644 index 0000000000..de5af5c549 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnumUnitCase_construct.phpt @@ -0,0 +1,36 @@ +--TEST-- +ReflectionEnumUnitCase::__construct() +--FILE-- +<?php + +enum Foo { + case Bar; + const Baz = self::Bar; +} + +echo (new ReflectionEnumUnitCase(Foo::class, 'Bar'))->getName() . "\n"; + +try { + new ReflectionEnumUnitCase(Foo::class, 'Baz'); +} catch (\Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + new ReflectionEnumUnitCase(Foo::class, 'Qux'); +} catch (\Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + new ReflectionEnumUnitCase([], 'Foo'); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Bar +Constant Foo::Baz is not a case +Constant Foo::Qux does not exist +ReflectionEnumUnitCase::__construct(): Argument #1 ($class) must be of type object|string, array given diff --git a/ext/reflection/tests/ReflectionEnumUnitCase_getEnum.phpt b/ext/reflection/tests/ReflectionEnumUnitCase_getEnum.phpt new file mode 100644 index 0000000000..3d3bcc227e --- /dev/null +++ b/ext/reflection/tests/ReflectionEnumUnitCase_getEnum.phpt @@ -0,0 +1,39 @@ +--TEST-- +ReflectionEnumUnitCase::getEnum() +--FILE-- +<?php + +enum Foo { + case Bar; +} + +echo (new ReflectionEnumUnitCase(Foo::class, 'Bar'))->getEnum(); + +?> +--EXPECTF-- +Class [ <user> final class Foo implements UnitEnum ] { + @@ %sReflectionEnumUnitCase_getEnum.php 3-5 + + - Constants [1] { + Constant [ public Foo Bar ] { Object } + } + + - Static properties [0] { + } + + - Static methods [1] { + Method [ <internal, prototype UnitEnum> static public method cases ] { + + - Parameters [0] { + } + - Return [ array ] + } + } + + - Properties [1] { + Property [ public string $name ] + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/ReflectionEnumUnitCase_getValue.phpt b/ext/reflection/tests/ReflectionEnumUnitCase_getValue.phpt new file mode 100644 index 0000000000..ec5f22d9f8 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnumUnitCase_getValue.phpt @@ -0,0 +1,30 @@ +--TEST-- +ReflectionEnumUnitCase::getValue() +--FILE-- +<?php + +enum Foo { + case Bar; + case Baz; +} + +$barFromReflection = (new ReflectionEnumUnitCase(Foo::class, 'Bar'))->getValue(); +$bazFromReflection = (new ReflectionEnumUnitCase(Foo::class, 'Baz'))->getValue(); + +var_dump($barFromReflection); +var_dump($bazFromReflection); + +var_dump(Foo::Bar === $barFromReflection); +var_dump(Foo::Baz === $barFromReflection); + +var_dump(Foo::Bar === $bazFromReflection); +var_dump(Foo::Baz === $bazFromReflection); + +?> +--EXPECT-- +enum(Foo::Bar) +enum(Foo::Baz) +bool(true) +bool(false) +bool(false) +bool(true) diff --git a/ext/reflection/tests/ReflectionEnum_construct.phpt b/ext/reflection/tests/ReflectionEnum_construct.phpt new file mode 100644 index 0000000000..4b4a039105 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_construct.phpt @@ -0,0 +1,34 @@ +--TEST-- +ReflectionEnum::__construct() +--FILE-- +<?php + +enum Foo {} +class Bar {} + +echo (new ReflectionEnum(Foo::class))->getName() . "\n"; + +try { + new ReflectionEnum('Bar'); +} catch (\Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + new ReflectionEnum('Baz'); +} catch (\Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + new ReflectionEnum([]); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Foo +Class "Bar" is not an enum +Class "Baz" does not exist +ReflectionEnum::__construct(): Argument #1 ($objectOrClass) must be of type object|string, array given diff --git a/ext/reflection/tests/ReflectionEnum_getBackingType.phpt b/ext/reflection/tests/ReflectionEnum_getBackingType.phpt new file mode 100644 index 0000000000..e9d429f592 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_getBackingType.phpt @@ -0,0 +1,20 @@ +--TEST-- +ReflectionEnum::getBackingType() +--FILE-- +<?php + +enum Enum_ {} +enum IntEnum: int {} +enum StringEnum: string {} + +function test(): string {} + +var_dump((new ReflectionEnum(Enum_::class))->getBackingType()); +echo (new ReflectionEnum(IntEnum::class))->getBackingType() . "\n"; +echo (new ReflectionEnum(StringEnum::class))->getBackingType() . "\n"; + +?> +--EXPECT-- +NULL +int +string diff --git a/ext/reflection/tests/ReflectionEnum_getCase.phpt b/ext/reflection/tests/ReflectionEnum_getCase.phpt new file mode 100644 index 0000000000..f2b53cf657 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_getCase.phpt @@ -0,0 +1,50 @@ +--TEST-- +ReflectionEnum::getCases() +--FILE-- +<?php + +enum Enum_ { + case Foo; + const Bar = self::Foo; +} + +enum IntEnum: int { + case Foo = 0; + const Bar = self::Foo; +} + +function test(string $enumName, string $caseName) { + try { + $reflectionEnum = new ReflectionEnum($enumName); + var_dump($reflectionEnum->getCase($caseName)); + } catch (Throwable $e) { + echo get_class($e) . ': ' . $e->getMessage() . "\n"; + } +} + +test(Enum_::class, 'Foo'); +test(Enum_::class, 'Bar'); +test(Enum_::class, 'Baz'); + +test(IntEnum::class, 'Foo'); +test(IntEnum::class, 'Bar'); +test(IntEnum::class, 'Baz'); + +?> +--EXPECT-- +object(ReflectionEnumUnitCase)#2 (2) { + ["name"]=> + string(3) "Foo" + ["class"]=> + string(5) "Enum_" +} +ReflectionException: Enum_::Bar is not a case +ReflectionException: Case Enum_::Baz does not exist +object(ReflectionEnumBackedCase)#2 (2) { + ["name"]=> + string(3) "Foo" + ["class"]=> + string(7) "IntEnum" +} +ReflectionException: IntEnum::Bar is not a case +ReflectionException: Case IntEnum::Baz does not exist diff --git a/ext/reflection/tests/ReflectionEnum_getCases.phpt b/ext/reflection/tests/ReflectionEnum_getCases.phpt new file mode 100644 index 0000000000..9365008774 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_getCases.phpt @@ -0,0 +1,54 @@ +--TEST-- +ReflectionEnum::getCases() +--FILE-- +<?php + +enum Enum_ { + case Foo; + case Bar; + const Baz = self::Bar; +} + +enum IntEnum: int { + case Foo = 0; + case Bar = 1; + const Baz = self::Bar; +} + +var_dump((new ReflectionEnum(Enum_::class))->getCases()); +var_dump((new ReflectionEnum(IntEnum::class))->getCases()); + +?> +--EXPECT-- +array(2) { + [0]=> + object(ReflectionEnumUnitCase)#2 (2) { + ["name"]=> + string(3) "Foo" + ["class"]=> + string(5) "Enum_" + } + [1]=> + object(ReflectionEnumUnitCase)#3 (2) { + ["name"]=> + string(3) "Bar" + ["class"]=> + string(5) "Enum_" + } +} +array(2) { + [0]=> + object(ReflectionEnumBackedCase)#2 (2) { + ["name"]=> + string(3) "Foo" + ["class"]=> + string(7) "IntEnum" + } + [1]=> + object(ReflectionEnumBackedCase)#1 (2) { + ["name"]=> + string(3) "Bar" + ["class"]=> + string(7) "IntEnum" + } +} diff --git a/ext/reflection/tests/ReflectionEnum_hasCase.phpt b/ext/reflection/tests/ReflectionEnum_hasCase.phpt new file mode 100644 index 0000000000..3310f0ef6b --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_hasCase.phpt @@ -0,0 +1,20 @@ +--TEST-- +ReflectionEnum::hasCase() +--FILE-- +<?php + +enum Foo { + case Bar; + const Baz = self::Bar; +} + +$reflectionEnum = new ReflectionEnum(Foo::class); +var_dump($reflectionEnum->hasCase('Bar')); +var_dump($reflectionEnum->hasCase('Baz')); +var_dump($reflectionEnum->hasCase('Qux')); + +?> +--EXPECT-- +bool(true) +bool(false) +bool(false) diff --git a/ext/reflection/tests/ReflectionEnum_isBacked.phpt b/ext/reflection/tests/ReflectionEnum_isBacked.phpt new file mode 100644 index 0000000000..cb449e951a --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_isBacked.phpt @@ -0,0 +1,20 @@ +--TEST-- +ReflectionEnum::isBacked() +--FILE-- +<?php + +enum Enum_ {} +enum IntEnum: int {} +enum StringEnum: string {} + +function test(): string {} + +var_dump((new ReflectionEnum(Enum_::class))->isBacked()); +var_dump((new ReflectionEnum(IntEnum::class))->isBacked()); +var_dump((new ReflectionEnum(StringEnum::class))->isBacked()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/ext/reflection/tests/ReflectionEnum_toString.phpt b/ext/reflection/tests/ReflectionEnum_toString.phpt new file mode 100644 index 0000000000..567191e594 --- /dev/null +++ b/ext/reflection/tests/ReflectionEnum_toString.phpt @@ -0,0 +1,39 @@ +--TEST-- +ReflectionEnum::__toString() +--FILE-- +<?php + +enum Foo { + case Bar; +} + +echo new ReflectionEnum(Foo::class); + +?> +--EXPECTF-- +Class [ <user> final class Foo implements UnitEnum ] { + @@ %sReflectionEnum_toString.php 3-5 + + - Constants [1] { + Constant [ public Foo Bar ] { Object } + } + + - Static properties [0] { + } + + - Static methods [1] { + Method [ <internal, prototype UnitEnum> static public method cases ] { + + - Parameters [0] { + } + - Return [ array ] + } + } + + - Properties [1] { + Property [ public string $name ] + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 81d3c8d55b..084e05e118 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection'); var_dump($ext->getClasses()); ?> --EXPECT-- -array(19) { +array(22) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -104,4 +104,19 @@ array(19) { ["name"]=> string(19) "ReflectionAttribute" } + ["ReflectionEnum"]=> + object(ReflectionClass)#21 (1) { + ["name"]=> + string(14) "ReflectionEnum" + } + ["ReflectionEnumUnitCase"]=> + object(ReflectionClass)#22 (1) { + ["name"]=> + string(22) "ReflectionEnumUnitCase" + } + ["ReflectionEnumBackedCase"]=> + object(ReflectionClass)#23 (1) { + ["name"]=> + string(24) "ReflectionEnumBackedCase" + } } diff --git a/ext/reflection/tests/ReflectionProperty_setValue_readonly.phpt b/ext/reflection/tests/ReflectionProperty_setValue_readonly.phpt new file mode 100644 index 0000000000..6efea38913 --- /dev/null +++ b/ext/reflection/tests/ReflectionProperty_setValue_readonly.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test ReflectionProperty::setValue() error cases. +--FILE-- +<?php + +enum Foo: int { + case Bar = 0; +} + +$reflection = new ReflectionProperty(Foo::class, 'value'); + +try { + $reflection->setValue(Foo::Bar, 1); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +var_dump(Foo::Bar->value); + +?> +--EXPECT-- +Enum properties are immutable +int(0) diff --git a/ext/reflection/tests/bug36337.phpt b/ext/reflection/tests/bug36337.phpt index ebe64431a7..d4a155bda9 100644 --- a/ext/reflection/tests/bug36337.phpt +++ b/ext/reflection/tests/bug36337.phpt @@ -3,7 +3,7 @@ Reflection Bug #36337 (ReflectionProperty fails to return correct visibility) --FILE-- <?php -abstract class enum { +abstract class enum_ { protected $_values; public function __construct() { @@ -13,7 +13,7 @@ abstract class enum { } -final class myEnum extends enum { +final class myEnum extends enum_ { public $_values = array( 0 => 'No value', ); |