summaryrefslogtreecommitdiff
path: root/ext/reflection/php_reflection.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/reflection/php_reflection.c')
-rw-r--r--ext/reflection/php_reflection.c255
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;