From a75c195000b3226904103244fa9c3d0ce1111838 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 8 Dec 2015 12:40:42 +0300 Subject: [PATCH] Implemented the RFC `Support Class Constant Visibility`. Squashed commit of the following: commit f11ca0e7a57793fa0e3e7f6d451720e6c42bb0b9 Author: Dmitry Stogov Date: Tue Dec 8 12:38:42 2015 +0300 Fixed test expectation commit 211f873f542504d0a0f72b6b5cb23908a1c99a2d Author: Dmitry Stogov Date: Tue Dec 8 12:28:38 2015 +0300 Embed zend_class_constant.flags into zend_class_constants.value.u2.access_flags commit 51deab84b2cdbf9cdb1a838cf33b2ee45c61748b Author: Dmitry Stogov Date: Mon Dec 7 11:18:55 2015 +0300 Fixed issues found by Nikita commit 544dbd5b47e40d38a8ccb96bc5583e9cb7fdd723 Author: Dmitry Stogov Date: Sat Dec 5 02:41:05 2015 +0300 Refactored immplementation of https://wiki.php.net/rfc/class_const_visibility @reeze created an RFC here and I emailed internals here and didn't get any responses positive/negative. --- Zend/zend_API.c | 42 ++- Zend/zend_API.h | 1 + Zend/zend_ast.h | 2 +- Zend/zend_compile.c | 41 ++- Zend/zend_compile.h | 6 + Zend/zend_constants.c | 27 +- Zend/zend_constants.h | 1 + Zend/zend_inheritance.c | 74 ++-- Zend/zend_language_parser.y | 8 +- Zend/zend_opcode.c | 24 +- Zend/zend_types.h | 4 + Zend/zend_vm_def.h | 11 +- Zend/zend_vm_execute.h | 49 ++- ext/opcache/Optimizer/pass1_5.c | 8 +- ext/opcache/zend_accelerator_util_funcs.c | 11 +- ext/opcache/zend_file_cache.c | 46 ++- ext/opcache/zend_persist.c | 35 +- ext/opcache/zend_persist_calc.c | 17 +- ext/reflection/php_reflection.c | 348 +++++++++++++++++- ext/reflection/tests/017.phpt | 4 +- .../tests/ReflectionClassConstant_basic1.phpt | 194 ++++++++++ .../tests/ReflectionClass_toString_001.phpt | 21 +- .../ReflectionExtension_getClasses_basic.phpt | 11 +- ext/reflection/tests/bug29986.phpt | 10 +- ext/reflection/tests/bug45765.phpt | 2 +- tests/classes/constants_comments_001.phpt | 34 ++ tests/classes/constants_visibility_001.phpt | 23 ++ tests/classes/constants_visibility_002.phpt | 30 ++ tests/classes/constants_visibility_003.phpt | 30 ++ tests/classes/constants_visibility_004.phpt | 28 ++ tests/classes/constants_visibility_005.phpt | 10 + tests/classes/constants_visibility_006.phpt | 11 + tests/classes/constants_visibility_007.phpt | 10 + .../constants_visibility_error_001.phpt | 16 + .../constants_visibility_error_002.phpt | 16 + .../constants_visibility_error_003.phpt | 16 + .../constants_visibility_error_004.phpt | 16 + .../interface_constant_inheritance_005.phpt | 12 + .../interface_constant_inheritance_006.phpt | 10 + .../interface_constant_inheritance_007.phpt | 10 + 40 files changed, 1143 insertions(+), 126 deletions(-) create mode 100644 ext/reflection/tests/ReflectionClassConstant_basic1.phpt create mode 100644 tests/classes/constants_comments_001.phpt create mode 100644 tests/classes/constants_visibility_001.phpt create mode 100644 tests/classes/constants_visibility_002.phpt create mode 100644 tests/classes/constants_visibility_003.phpt create mode 100644 tests/classes/constants_visibility_004.phpt create mode 100644 tests/classes/constants_visibility_005.phpt create mode 100644 tests/classes/constants_visibility_006.phpt create mode 100644 tests/classes/constants_visibility_007.phpt create mode 100644 tests/classes/constants_visibility_error_001.phpt create mode 100644 tests/classes/constants_visibility_error_002.phpt create mode 100644 tests/classes/constants_visibility_error_003.phpt create mode 100644 tests/classes/constants_visibility_error_004.phpt create mode 100644 tests/classes/interface_constant_inheritance_005.phpt create mode 100644 tests/classes/interface_constant_inheritance_006.phpt create mode 100644 tests/classes/interface_constant_inheritance_007.phpt diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 38b6426bec..fc49f3369e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1129,12 +1129,13 @@ ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */ zend_class_entry **scope = EG(current_execute_data) ? &EG(scope) : &CG(active_class_entry); zend_class_entry *old_scope = *scope; zend_class_entry *ce; + zend_class_constant *c; zval *val; zend_property_info *prop_info; *scope = class_type; - ZEND_HASH_FOREACH_VAL(&class_type->constants_table, val) { - ZVAL_DEREF(val); + ZEND_HASH_FOREACH_PTR(&class_type->constants_table, c) { + val = &c->value; if (Z_CONSTANT_P(val)) { if (UNEXPECTED(zval_update_constant_ex(val, 1, class_type) != SUCCESS)) { return FAILURE; @@ -3737,16 +3738,49 @@ ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *nam } /* }}} */ -ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */ +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */ { + zend_class_constant *c; + + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + if (access_type != ZEND_ACC_PUBLIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface constant %s::%s must be public", ZSTR_VAL(ce->name), ZSTR_VAL(name)); + } + } + + if (zend_string_equals_literal_ci(name, "class")) { + zend_error((ce->type == ZEND_INTERNAL_CLASS) ? E_CORE_ERROR : E_COMPILE_ERROR, + "A class constant must not be called 'class'; it is reserved for class name fetching"); + } + + if (ce->type == ZEND_INTERNAL_CLASS) { + c = pemalloc(sizeof(zend_class_constant), 1); + } else { + c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + } + ZVAL_COPY_VALUE(&c->value, value); + Z_ACCESS_FLAGS(c->value) = access_type; + c->doc_comment = doc_comment; + c->ce = ce; if (Z_CONSTANT_P(value)) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } - return zend_hash_str_update(&ce->constants_table, name, name_length, value) ? + return zend_hash_add_ptr(&ce->constants_table, name, c) ? SUCCESS : FAILURE; } /* }}} */ +ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */ +{ + int ret; + + zend_string *key = zend_string_init(name, name_length, ce->type & ZEND_INTERNAL_CLASS); + ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(key); + return ret; +} +/* }}} */ + ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length) /* {{{ */ { zval constant; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 7b511d16ab..055e2628b6 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -324,6 +324,7 @@ ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type); ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type); +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 2defa1c2b3..1a4a4ce707 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -124,7 +124,6 @@ enum _zend_ast_kind { ZEND_AST_SWITCH, ZEND_AST_SWITCH_CASE, ZEND_AST_DECLARE, - ZEND_AST_CONST_ELEM, ZEND_AST_USE_TRAIT, ZEND_AST_TRAIT_PRECEDENCE, ZEND_AST_METHOD_REFERENCE, @@ -142,6 +141,7 @@ enum _zend_ast_kind { ZEND_AST_CATCH, ZEND_AST_PARAM, ZEND_AST_PROP_ELEM, + ZEND_AST_CONST_ELEM, /* 4 child nodes */ ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 65538094b7..1e38cafc89 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -97,6 +97,12 @@ static void zend_destroy_property_info_internal(zval *zv) /* {{{ */ } /* }}} */ +static void zend_destroy_class_constant_internal(zval *zv) /* {{{ */ +{ + free(Z_PTR_P(zv)); +} +/* }}} */ + static zend_string *zend_new_interned_string_safe(zend_string *str) /* {{{ */ { zend_string *interned_str; @@ -1437,14 +1443,15 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ { uint32_t fetch_type = zend_get_class_fetch_type(class_name); + zend_class_constant *cc; zval *c; if (class_name_refers_to_active_ce(class_name, fetch_type)) { - c = zend_hash_find(&CG(active_class_entry)->constants_table, name); + cc = zend_hash_find_ptr(&CG(active_class_entry)->constants_table, name); } else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); if (ce) { - c = zend_hash_find(&ce->constants_table, name); + cc = zend_hash_find_ptr(&ce->constants_table, name); } else { return 0; } @@ -1456,8 +1463,14 @@ static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, return 0; } + if (!cc || !zend_verify_const_access(cc, CG(active_class_entry))) { + return 0; + } + + c = &cc->value; + /* Substitute case-sensitive (or lowercase) persistent class constants */ - if (c && Z_TYPE_P(c) < IS_OBJECT) { + if (Z_TYPE_P(c) < IS_OBJECT) { ZVAL_DUP(zv, c); return 1; } @@ -1638,7 +1651,6 @@ again: ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers) /* {{{ */ { zend_bool persistent_hashes = (ce->type == ZEND_INTERNAL_CLASS) ? 1 : 0; - dtor_func_t zval_ptr_dtor_func = ((persistent_hashes) ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR); ce->refcount = 1; ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED; @@ -1650,7 +1662,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify ce->default_properties_table = NULL; ce->default_static_members_table = NULL; zend_hash_init_ex(&ce->properties_info, 8, NULL, (persistent_hashes ? zend_destroy_property_info_internal : NULL), persistent_hashes, 0); - zend_hash_init_ex(&ce->constants_table, 8, NULL, zval_ptr_dtor_func, persistent_hashes, 0); + zend_hash_init_ex(&ce->constants_table, 8, NULL, (persistent_hashes ? zend_destroy_class_constant_internal : NULL), persistent_hashes, 0); zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0); if (ce->type == ZEND_INTERNAL_CLASS) { @@ -5183,25 +5195,28 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; zend_ast *value_ast = const_ast->child[1]; + zend_ast *doc_comment_ast = const_ast->child[2]; zend_string *name = zend_ast_get_str(name_ast); + zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; zval value_zv; - if (zend_string_equals_literal_ci(name, "class")) { - zend_error(E_COMPILE_ERROR, - "A class constant must not be called 'class'; it is reserved for class name fetching"); + if (UNEXPECTED(ast->attr & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) { + if (ast->attr & ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'static' as constant modifier"); + } else if (ast->attr & ZEND_ACC_ABSTRACT) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'abstract' as constant modifier"); + } else if (ast->attr & ZEND_ACC_FINAL) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'final' as constant modifier"); + } } zend_const_expr_to_zval(&value_zv, value_ast); name = zend_new_interned_string_safe(name); - if (zend_hash_add(&ce->constants_table, name, &value_zv) == NULL) { + if (zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment) != SUCCESS) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot redefine class constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(name)); } - - if (Z_CONSTANT(value_zv)) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } } } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 711fc00009..c4a5053ba6 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -310,6 +310,12 @@ typedef struct _zend_property_info { #define OBJ_PROP_TO_NUM(offset) \ ((offset - OBJ_PROP_TO_OFFSET(0)) / sizeof(zval)) +typedef struct _zend_class_constant { + zval value; /* access flags are stored in reserved: zval.u2.access_flags */ + zend_string *doc_comment; + zend_class_entry *ce; +} zend_class_constant; + /* arg_info for internal functions */ typedef struct _zend_internal_arg_info { const char *name; diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 159379d71c..d539445be7 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -251,6 +251,18 @@ static zend_constant *zend_get_special_constant(const char *name, size_t name_le } } +ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *scope) /* {{{ */ +{ + if (Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PUBLIC) { + return 1; + } else if (Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PRIVATE) { + return (c->ce == scope); + } else { + ZEND_ASSERT(Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PROTECTED); + return zend_check_protected(c->ce, scope); + } +} +/* }}} */ ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len) { @@ -360,16 +372,23 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, } free_alloca(lcname, use_heap); if (ce) { - ret_constant = zend_hash_find(&ce->constants_table, constant_name); - if (ret_constant == NULL) { + zend_class_constant *c = zend_hash_find_ptr(&ce->constants_table, constant_name); + if (c == NULL) { if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { zend_throw_error(NULL, "Undefined class constant '%s::%s'", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); zend_string_release(class_name); zend_string_free(constant_name); return NULL; } - } else if (Z_ISREF_P(ret_constant)) { - ret_constant = Z_REFVAL_P(ret_constant); + ret_constant = NULL; + } else { + if (!zend_verify_const_access(c, scope)) { + zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); + zend_string_release(class_name); + zend_string_free(constant_name); + return NULL; + } + ret_constant = &c->value; } } zend_string_release(class_name); diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 2f8b7288d7..35324214fa 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -65,6 +65,7 @@ int zend_startup_constants(void); int zend_shutdown_constants(void); void zend_register_standard_constants(void); void clean_non_persistent_constants(void); +ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *ce); ZEND_API zval *zend_get_constant(zend_string *name); ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len); ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 73b67f5216..d9367d82f5 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -698,21 +698,29 @@ ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_ } /* }}} */ -static void do_inherit_class_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */ +static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */ { - if (!zend_hash_exists(&ce->constants_table, name)) { - if (!Z_ISREF_P(zv)) { - if (parent_ce->type == ZEND_INTERNAL_CLASS) { - ZVAL_NEW_PERSISTENT_REF(zv, zv); - } else { - ZVAL_NEW_REF(zv, zv); - } + zend_class_constant *c = zend_hash_find_ptr(&ce->constants_table, name); + + if (c != NULL) { + if (UNEXPECTED((Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PPP_MASK) > (Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PPP_MASK))) { + zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s", + ZSTR_VAL(ce->name), ZSTR_VAL(name), zend_visibility_string(Z_ACCESS_FLAGS(parent_const->value)), ZSTR_VAL(ce->parent->name), (Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PUBLIC) ? "" : " or weaker"); } - if (Z_CONSTANT_P(Z_REFVAL_P(zv))) { + } else if (!(Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PRIVATE)) { + if (Z_CONSTANT(parent_const->value)) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } - Z_ADDREF_P(zv); - _zend_hash_append(&ce->constants_table, name, zv); + if (Z_REFCOUNTED(parent_const->value)) { + Z_ADDREF(parent_const->value); + } + if (ce->type & ZEND_INTERNAL_CLASS) { + c = pemalloc(sizeof(zend_class_constant), 1); + } else { + c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + } + memcpy(c, parent_const, sizeof(zend_class_constant)); + _zend_hash_append_ptr(&ce->constants_table, name, c); } } /* }}} */ @@ -722,7 +730,6 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent zend_property_info *property_info; zend_function *func; zend_string *key; - zval *zv; if (UNEXPECTED(ce->ce_flags & ZEND_ACC_INTERFACE)) { /* Interface can only inherit other interfaces */ @@ -859,12 +866,14 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent } if (zend_hash_num_elements(&parent_ce->constants_table)) { + zend_class_constant *c; + zend_hash_extend(&ce->constants_table, zend_hash_num_elements(&ce->constants_table) + zend_hash_num_elements(&parent_ce->constants_table), 0); - ZEND_HASH_FOREACH_STR_KEY_VAL(&parent_ce->constants_table, key, zv) { - do_inherit_class_constant(key, zv, ce, parent_ce); + ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->constants_table, key, c) { + do_inherit_class_constant(key, c, ce); } ZEND_HASH_FOREACH_END(); } @@ -894,14 +903,12 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent } /* }}} */ -static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zval *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ +static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ { - zval *old_constant; + zend_class_constant *old_constant; - if ((old_constant = zend_hash_find(child_constants_table, name)) != NULL) { - if (!Z_ISREF_P(old_constant) || - !Z_ISREF_P(parent_constant) || - Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) { + if ((old_constant = zend_hash_find_ptr(child_constants_table, name)) != NULL) { + if (old_constant->ce != parent_constant->ce) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", ZSTR_VAL(name), ZSTR_VAL(iface->name)); } return 0; @@ -910,21 +917,16 @@ static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zva } /* }}} */ -static void do_inherit_iface_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */ +static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */ { - if (do_inherit_constant_check(&ce->constants_table, zv, name, iface)) { - if (!Z_ISREF_P(zv)) { - if (iface->type == ZEND_INTERNAL_CLASS) { - ZVAL_NEW_PERSISTENT_REF(zv, zv); - } else { - ZVAL_NEW_REF(zv, zv); - } + if (do_inherit_constant_check(&ce->constants_table, c, name, iface)) { + if (Z_REFCOUNTED(c->value)) { + Z_ADDREF(c->value); } - Z_ADDREF_P(zv); - if (Z_CONSTANT_P(Z_REFVAL_P(zv))) { + if (Z_CONSTANT(c->value)) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } - zend_hash_update(&ce->constants_table, name, zv); + zend_hash_update_ptr(&ce->constants_table, name, c); } } /* }}} */ @@ -936,7 +938,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0; zend_function *func; zend_string *key; - zval *zv; + zend_class_constant *c; for (i = 0; i < ce->num_interfaces; i++) { if (ce->interfaces[i] == NULL) { @@ -952,8 +954,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } if (ignore) { /* Check for attempt to redeclare interface constants */ - ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, zv) { - do_inherit_constant_check(&iface->constants_table, zv, key, iface); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { + do_inherit_constant_check(&iface->constants_table, c, key, iface); } ZEND_HASH_FOREACH_END(); } else { if (ce->num_interfaces >= current_iface_num) { @@ -965,8 +967,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } ce->interfaces[ce->num_interfaces++] = iface; - ZEND_HASH_FOREACH_STR_KEY_VAL(&iface->constants_table, key, zv) { - do_inherit_iface_constant(key, zv, ce, iface); + ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { + do_inherit_iface_constant(key, c, ce, iface); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 3fc6494702..3ff896d4a4 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -701,8 +701,8 @@ class_statement_list: class_statement: variable_modifiers property_list ';' { $$ = $2; $$->attr = $1; } - | T_CONST class_const_list ';' - { $$ = $2; RESET_DOC_COMMENT(); } + | method_modifiers T_CONST class_const_list ';' + { $$ = $3; $$->attr = $1; } | T_USE name_list trait_adaptations { $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); } | method_modifiers function returns_ref identifier '(' parameter_list ')' @@ -810,11 +810,11 @@ class_const_list: ; class_const_decl: - identifier '=' expr { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3); } + identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } ; const_decl: - T_STRING '=' expr { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3); } + T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } ; echo_expr_list: diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 86fe4020a7..f6ac06e04f 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -287,7 +287,17 @@ ZEND_API void destroy_zend_class(zval *zv) zend_hash_destroy(&ce->properties_info); zend_string_release(ce->name); zend_hash_destroy(&ce->function_table); - zend_hash_destroy(&ce->constants_table); + if (zend_hash_num_elements(&ce->constants_table)) { + zend_class_constant *c; + + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + zval_ptr_dtor(&c->value); + if (c->doc_comment && c->ce == ce) { + zend_string_release(c->doc_comment); + } + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(&ce->constants_table); + } if (ce->num_interfaces > 0 && ce->interfaces) { efree(ce->interfaces); } @@ -322,7 +332,17 @@ ZEND_API void destroy_zend_class(zval *zv) zend_hash_destroy(&ce->properties_info); zend_string_release(ce->name); zend_hash_destroy(&ce->function_table); - zend_hash_destroy(&ce->constants_table); + if (zend_hash_num_elements(&ce->constants_table)) { + zend_class_constant *c; + + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + zval_internal_ptr_dtor(&c->value); + if (c->doc_comment && c->ce == ce) { + zend_string_release(c->doc_comment); + } + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(&ce->constants_table); + } if (ce->num_interfaces > 0) { free(ce->interfaces); } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 805ea6d8c7..4155f7530d 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -138,6 +138,7 @@ struct _zval_struct { uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ + uint32_t access_flags; /* class constant access flags */ } u2; }; @@ -361,6 +362,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { #define Z_FE_ITER(zval) (zval).u2.fe_iter_idx #define Z_FE_ITER_P(zval_p) Z_FE_ITER(*(zval_p)) +#define Z_ACCESS_FLAGS(zval) (zval).u2.access_flags +#define Z_ACCESS_FLAGS_P(zval_p) Z_ACCESS_FLAGS(*(zval_p)) + #define Z_COUNTED(zval) (zval).value.counted #define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 74f19b612d..e4ec00a45c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5163,6 +5163,7 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, UNUSED, CONST, CONST_FETCH) ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED, CONST) { zend_class_entry *ce; + zend_class_constant *c; zval *value; USE_OPLINE @@ -5172,7 +5173,6 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED, CONST) if (OP1_TYPE == IS_CONST) { if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) { value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); #ifdef ZTS ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); #endif @@ -5200,13 +5200,16 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED, CONST) ce = Z_CE_P(EX_VAR(opline->op1.var)); } if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); break; } } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); + if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + if (!zend_verify_const_access(c, EG(scope))) { + zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); + } + value = &c->value; if (Z_CONSTANT_P(value)) { EG(scope) = ce; zval_update_constant_ex(value, 1, NULL); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5815fe82f4..f466dc12db 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5824,6 +5824,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CONST_HANDLER( static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_class_entry *ce; + zend_class_constant *c; zval *value; USE_OPLINE @@ -5833,7 +5834,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS if (IS_CONST == IS_CONST) { if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) { value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); #ifdef ZTS ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); #endif @@ -5861,13 +5861,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS ce = Z_CE_P(EX_VAR(opline->op1.var)); } if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); break; } } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); + if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + if (!zend_verify_const_access(c, EG(scope))) { + zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); + } + value = &c->value; if (Z_CONSTANT_P(value)) { EG(scope) = ce; zval_update_constant_ex(value, 1, NULL); @@ -17546,6 +17549,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_class_entry *ce; + zend_class_constant *c; zval *value; USE_OPLINE @@ -17555,7 +17559,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ if (IS_VAR == IS_CONST) { if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) { value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); #ifdef ZTS ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); #endif @@ -17583,13 +17586,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ ce = Z_CE_P(EX_VAR(opline->op1.var)); } if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); break; } } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); + if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + if (!zend_verify_const_access(c, EG(scope))) { + zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); + } + value = &c->value; if (Z_CONSTANT_P(value)) { EG(scope) = ce; zval_update_constant_ex(value, 1, NULL); @@ -23883,8 +23889,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) { if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) { object = Z_OBJ(EX(This)); - } - if (!object) { + ce = object->ce; + } else { if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { /* Allowed for PHP 4 compatibility. */ zend_error( @@ -23969,6 +23975,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CON static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_class_entry *ce; + zend_class_constant *c; zval *value; USE_OPLINE @@ -23978,7 +23985,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS if (IS_UNUSED == IS_CONST) { if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) { value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); #ifdef ZTS ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); #endif @@ -24006,13 +24012,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS ce = Z_CE_P(EX_VAR(opline->op1.var)); } if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); break; } } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); + if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + if (!zend_verify_const_access(c, EG(scope))) { + zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); + } + value = &c->value; if (Z_CONSTANT_P(value)) { EG(scope) = ce; zval_update_constant_ex(value, 1, NULL); @@ -25234,8 +25243,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) { if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) { object = Z_OBJ(EX(This)); - } - if (!object) { + ce = object->ce; + } else { if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { /* Allowed for PHP 4 compatibility. */ zend_error( @@ -26592,8 +26601,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) { if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) { object = Z_OBJ(EX(This)); - } - if (!object) { + ce = object->ce; + } else { if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { /* Allowed for PHP 4 compatibility. */ zend_error( @@ -28225,8 +28234,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) { if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) { object = Z_OBJ(EX(This)); - } - if (!object) { + ce = object->ce; + } else { if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { /* Allowed for PHP 4 compatibility. */ zend_error( diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 4abd031839..a4c030aff8 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -324,11 +324,13 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) if (ce) { uint32_t tv = ZEND_RESULT(opline).var; + zend_class_constant *cc; zval *c, t; - if ((c = zend_hash_find(&ce->constants_table, - Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL) { - ZVAL_DEREF(c); + if ((cc = zend_hash_find_ptr(&ce->constants_table, + Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL && + (Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) { + c = &cc->value; if (Z_TYPE_P(c) == IS_CONSTANT_AST) { break; } diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index ab048c6df3..f9bb6a11cc 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -230,6 +230,7 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source) { Bucket *p, *q, *end; zend_ulong nIndex; + zend_class_constant *c; ht->nTableSize = source->nTableSize; ht->nTableMask = source->nTableMask; @@ -265,8 +266,14 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source) q->key = p->key; /* Copy data */ - ZVAL_COPY_VALUE(&q->val, &p->val); - zend_clone_zval(&q->val); + c = ARENA_REALLOC(Z_PTR(p->val)); + ZVAL_PTR(&q->val, c); + + zend_clone_zval(&c->value); + if ((void*)c->ce >= ZCG(current_persistent_script)->arena_mem && + (void*)c->ce < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) { + c->ce = ARENA_REALLOC(c->ce); + } } } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index ab450b0fe9..721bac933a 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -508,6 +508,28 @@ static void zend_file_cache_serialize_prop_info(zval *zv, } } +static void zend_file_cache_serialize_class_constant(zval *zv, + zend_persistent_script *script, + zend_file_cache_metainfo *info, + void *buf) +{ + if (!IS_SERIALIZED(Z_PTR_P(zv))) { + zend_class_constant *c; + + SERIALIZE_PTR(Z_PTR_P(zv)); + c = Z_PTR_P(zv); + UNSERIALIZE_PTR(c); + + zend_file_cache_serialize_zval(&c->value, script, info, buf); + if (c->ce && !IS_SERIALIZED(c->ce)) { + SERIALIZE_PTR(c->ce); + } + if (c->doc_comment && !IS_SERIALIZED(c->doc_comment)) { + SERIALIZE_STR(c->doc_comment); + } + } +} + static void zend_file_cache_serialize_class(zval *zv, zend_persistent_script *script, zend_file_cache_metainfo *info, @@ -545,7 +567,7 @@ static void zend_file_cache_serialize_class(zval *zv, p++; } } - zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_zval); + zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant); SERIALIZE_STR(ce->info.user.filename); SERIALIZE_STR(ce->info.user.doc_comment); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); @@ -1055,6 +1077,26 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, } } +static void zend_file_cache_unserialize_class_constant(zval *zv, + zend_persistent_script *script, + void *buf) +{ + if (!IS_UNSERIALIZED(Z_PTR_P(zv))) { + zend_class_constant *c; + + UNSERIALIZE_PTR(Z_PTR_P(zv)); + c = Z_PTR_P(zv); + + zend_file_cache_unserialize_class_constant(&c->value, script, buf); + if (c->ce && !IS_UNSERIALIZED(c->ce)) { + UNSERIALIZE_PTR(c->ce); + } + if (c->doc_comment && !IS_UNSERIALIZED(c->doc_comment)) { + UNSERIALIZE_STR(c->doc_comment); + } + } +} + static void zend_file_cache_unserialize_class(zval *zv, zend_persistent_script *script, void *buf) @@ -1090,7 +1132,7 @@ static void zend_file_cache_unserialize_class(zval *zv, } } zend_file_cache_unserialize_hash(&ce->constants_table, - script, buf, zend_file_cache_unserialize_zval, NULL); + script, buf, zend_file_cache_unserialize_class_constant, NULL); UNSERIALIZE_STR(ce->info.user.filename); UNSERIALIZE_STR(ce->info.user.doc_comment); zend_file_cache_unserialize_hash(&ce->properties_info, diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 11cf33ee4a..d7c56cdfe4 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -716,6 +716,39 @@ static void zend_persist_property_info(zval *zv) } } +static void zend_persist_class_constant(zval *zv) +{ + zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv)); + + if (c) { + Z_PTR_P(zv) = c; + return; + } + memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_constant)); + zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); + c = Z_PTR_P(zv) = ZCG(arena_mem); + ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_constant))); + zend_persist_zval(&c->value); + c->ce = zend_shared_alloc_get_xlat_entry(c->ce); + if (c->doc_comment) { + if (ZCG(accel_directives).save_comments) { + zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment); + if (doc_comment) { + c->doc_comment = doc_comment; + } else { + zend_accel_store_string(c->doc_comment); + } + } else { + zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment); + if (!doc_comment) { + zend_shared_alloc_register_xlat_entry(c->doc_comment, c->doc_comment); + zend_string_release(c->doc_comment); + } + c->doc_comment = NULL; + } + } +} + static void zend_persist_class_entry(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -745,7 +778,7 @@ static void zend_persist_class_entry(zval *zv) } ce->static_members_table = NULL; - zend_hash_persist(&ce->constants_table, zend_persist_zval); + zend_hash_persist(&ce->constants_table, zend_persist_class_constant); if (ce->info.user.filename) { /* do not free! PHP has centralized filename storage, compiler will free it */ diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index a3c2d1a905..0431054f76 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -292,6 +292,21 @@ static void zend_persist_property_info_calc(zval *zv) } } +static void zend_persist_class_constant_calc(zval *zv) +{ + zend_class_constant *c = Z_PTR_P(zv); + + if (!zend_shared_alloc_get_xlat_entry(c)) { + zend_shared_alloc_register_xlat_entry(c, c); + ADD_ARENA_SIZE(sizeof(zend_class_constant)); + zend_persist_zval_calc(&c->value); + if (ZCG(accel_directives).save_comments && c->doc_comment) { + ADD_STRING(c->doc_comment); + } + } +} + + static void zend_persist_class_entry_calc(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -316,7 +331,7 @@ static void zend_persist_class_entry_calc(zval *zv) zend_persist_zval_calc(&ce->default_static_members_table[i]); } } - zend_hash_persist_calc(&ce->constants_table, zend_persist_zval_calc); + zend_hash_persist_calc(&ce->constants_table, zend_persist_class_constant_calc); if (ce->info.user.filename) { ADD_STRING(ce->info.user.filename); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 80001e4997..51ed11710d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -64,6 +64,7 @@ PHPAPI zend_class_entry *reflection_class_ptr; PHPAPI zend_class_entry *reflection_object_ptr; PHPAPI zend_class_entry *reflection_method_ptr; PHPAPI zend_class_entry *reflection_property_ptr; +PHPAPI zend_class_entry *reflection_class_constant_ptr; PHPAPI zend_class_entry *reflection_extension_ptr; PHPAPI zend_class_entry *reflection_zend_extension_ptr; @@ -215,7 +216,8 @@ typedef enum { REF_TYPE_PARAMETER, REF_TYPE_TYPE, REF_TYPE_PROPERTY, - REF_TYPE_DYNAMIC_PROPERTY + REF_TYPE_DYNAMIC_PROPERTY, + REF_TYPE_CLASS_CONSTANT } reflection_type_t; /* Struct for reflection objects */ @@ -333,7 +335,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ efree(intern->ptr); break; case REF_TYPE_GENERATOR: - break; + case REF_TYPE_CLASS_CONSTANT: case REF_TYPE_OTHER: break; } @@ -368,6 +370,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{ static void _const_string(string *str, char *name, zval *value, char *indent); static void _function_string(string *str, zend_function *fptr, zend_class_entry *scope, char* indent); static void _property_string(string *str, zend_property_info *prop, char *prop_name, char* indent); +static void _class_const_string(string *str, char *name, zend_class_constant *c, char* indent); static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *indent); static void _extension_string(string *str, zend_module_entry *module, char *indent); static void _zend_extension_string(string *str, zend_extension *extension, char *indent); @@ -450,11 +453,11 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in string_printf(str, "%s - Constants [%d] {\n", indent, count); if (count > 0) { zend_string *key; - zval *value; + zend_class_constant *c; - ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, value) { - zval_update_constant_ex(value, 1, NULL); - _const_string(str, ZSTR_VAL(key), value, indent); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { + zval_update_constant_ex(&c->value, 1, NULL); + _class_const_string(str, ZSTR_VAL(key), c, indent); } ZEND_HASH_FOREACH_END(); } string_printf(str, "%s }\n", indent); @@ -627,6 +630,20 @@ static void _const_string(string *str, char *name, zval *value, char *indent) } /* }}} */ +/* {{{ _class_const_string */ +static void _class_const_string(string *str, char *name, zend_class_constant *c, char *indent) +{ + char *type = zend_zval_type_name(&c->value); + char *visibility = zend_visibility_string(Z_ACCESS_FLAGS(c->value)); + zend_string *value_str = zval_get_string(&c->value); + + string_printf(str, "%s Constant [ %s %s %s ] { %s }\n", + indent, visibility, type, name, ZSTR_VAL(value_str)); + + zend_string_release(value_str); +} +/* }}} */ + /* {{{ _get_recv_opcode */ static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset) { @@ -1356,6 +1373,27 @@ static void reflection_property_factory(zend_class_entry *ce, zend_property_info } /* }}} */ +/* {{{ reflection_class_constant_factory */ +static void reflection_class_constant_factory(zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object) +{ + reflection_object *intern; + zval name; + zval classname; + + ZVAL_STR_COPY(&name, name_str); + ZVAL_STR_COPY(&classname, ce->name); + + reflection_instantiate(reflection_class_constant_ptr, object); + intern = Z_REFLECTION_P(object); + intern->ptr = constant; + intern->ref_type = REF_TYPE_CLASS_CONSTANT; + intern->ce = constant->ce; + intern->ignore_visibility = 0; + reflection_update_property(object, "name", &name); + reflection_update_property(object, "class", &classname); +} +/* }}} */ + /* {{{ _reflection_export */ static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_ptr, int ctor_argc) { @@ -3668,6 +3706,197 @@ ZEND_METHOD(reflection_method, setAccessible) } /* }}} */ +/* {{{ proto public void ReflectionClassConstant::__construct(mixed class, string name) + Constructor. Throws an Exception in case the given class constant does not exist */ +ZEND_METHOD(reflection_class_constant, __construct) +{ + zval *classname, *object, name, cname; + zend_string *constname; + reflection_object *intern; + zend_class_entry *ce; + zend_class_constant *constant = NULL; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zS", &classname, &constname) == FAILURE) { + return; + } + + object = getThis(); + intern = Z_REFLECTION_P(object); + if (intern == NULL) { + return; + } + + /* Find the class entry */ + switch (Z_TYPE_P(classname)) { + case IS_STRING: + if ((ce = zend_lookup_class(Z_STR_P(classname))) == NULL) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Class %s does not exist", Z_STRVAL_P(classname)); + return; + } + break; + + case IS_OBJECT: + ce = Z_OBJCE_P(classname); + break; + + default: + _DO_THROW("The parameter class is expected to be either a string or an object"); + /* returns out of this function */ + } + + if ((constant = zend_hash_find_ptr(&ce->constants_table, constname)) == NULL) { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Class Constant %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(constname)); + return; + } + + ZVAL_STR_COPY(&name, constname); + ZVAL_STR_COPY(&cname, ce->name); + + intern->ptr = constant; + intern->ref_type = REF_TYPE_CLASS_CONSTANT; + intern->ce = constant->ce; + intern->ignore_visibility = 0; + reflection_update_property(object, "name", &name); + reflection_update_property(object, "class", &cname); +} +/* }}} */ + +/* {{{ proto public string ReflectionClassConstant::__toString() + Returns a string representation */ +ZEND_METHOD(reflection_class_constant, __toString) +{ + reflection_object *intern; + zend_class_constant *ref; + string str; + zval name; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + string_init(&str); + _default_get_entry(getThis(), "name", sizeof("name")-1, &name); + _class_const_string(&str, Z_STRVAL(name), ref, ""); + zval_ptr_dtor(&name); + RETURN_NEW_STR(str.buf); +} +/* }}} */ + +/* {{{ proto public string ReflectionClassConstant::getName() + Returns the constant' name */ +ZEND_METHOD(reflection_class_constant, getName) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + _default_get_entry(getThis(), "name", sizeof("name")-1, return_value); +} +/* }}} */ + +static void _class_constant_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */ +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + RETURN_BOOL(Z_ACCESS_FLAGS(ref->value) & mask); +} +/* }}} */ + +/* {{{ proto public bool ReflectionClassConstant::isPublic() + Returns whether this constant is public */ +ZEND_METHOD(reflection_class_constant, isPublic) +{ + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PUBLIC | ZEND_ACC_IMPLICIT_PUBLIC); +} +/* }}} */ + +/* {{{ proto public bool ReflectionClassConstant::isPrivate() + Returns whether this constant is private */ +ZEND_METHOD(reflection_class_constant, isPrivate) +{ + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PRIVATE); +} +/* }}} */ + +/* {{{ proto public bool ReflectionClassConstant::isProtected() + Returns whether this constant is protected */ +ZEND_METHOD(reflection_class_constant, isProtected) +{ + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PROTECTED); +} +/* }}} */ + +/* {{{ proto public int ReflectionClassConstant::getModifiers() + Returns a bitfield of the access modifiers for this constant */ +ZEND_METHOD(reflection_class_constant, getModifiers) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + + RETURN_LONG(Z_ACCESS_FLAGS(ref->value)); +} +/* }}} */ + +/* {{{ proto public mixed ReflectionClassConstant::getValue() + Returns this constant's value */ +ZEND_METHOD(reflection_class_constant, getValue) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + + ZVAL_DUP(return_value, &ref->value); +} +/* }}} */ + +/* {{{ proto public ReflectionClass ReflectionClassConstant::getDeclaringClass() + Get the declaring class */ +ZEND_METHOD(reflection_class_constant, getDeclaringClass) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + + zend_reflection_class_factory(ref->ce, return_value); +} +/* }}} */ + +/* {{{ proto public string ReflectionClassConstant::getDocComment() + Returns the doc comment for this constant */ +ZEND_METHOD(reflection_class_constant, getDocComment) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + if (ref->doc_comment) { + RETURN_STR_COPY(ref->doc_comment); + } + RETURN_FALSE; +} +/* }}} */ + /* {{{ proto public static mixed ReflectionClass::export(mixed argument [, bool return]) throws ReflectionException Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */ ZEND_METHOD(reflection_class, export) @@ -4414,6 +4643,8 @@ ZEND_METHOD(reflection_class, getConstants) { reflection_object *intern; zend_class_entry *ce; + zend_string *key; + zend_class_constant *c; zval *val; if (zend_parse_parameters_none() == FAILURE) { @@ -4421,12 +4652,36 @@ ZEND_METHOD(reflection_class, getConstants) } GET_REFLECTION_OBJECT_PTR(ce); array_init(return_value); - ZEND_HASH_FOREACH_VAL(&ce->constants_table, val) { - if (UNEXPECTED(zval_update_constant_ex(val, 1, ce) != SUCCESS)) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { + if (UNEXPECTED(zval_update_constant_ex(&c->value, 1, ce) != SUCCESS)) { + zend_array_destroy(Z_ARRVAL_P(return_value)); return; } + val = zend_hash_add_new(Z_ARRVAL_P(return_value), key, &c->value); + Z_TRY_ADDREF_P(val); + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ + +/* {{{ proto public array ReflectionClass::getReflectionConstants() + Returns an associative array containing this class' constants as ReflectionClassConstant objects */ +ZEND_METHOD(reflection_class, getReflectionConstants) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_string *name; + zend_class_constant *constant; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ce); + array_init(return_value); + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) { + zval class_const; + reflection_class_constant_factory(ce, name, constant, &class_const); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &class_const); } ZEND_HASH_FOREACH_END(); - zend_hash_copy(Z_ARRVAL_P(return_value), &ce->constants_table, zval_add_ref_unref); } /* }}} */ @@ -4436,7 +4691,7 @@ ZEND_METHOD(reflection_class, getConstant) { reflection_object *intern; zend_class_entry *ce; - zval *value; + zend_class_constant *c; zend_string *name; METHOD_NOTSTATIC(reflection_class_ptr); @@ -4445,15 +4700,36 @@ ZEND_METHOD(reflection_class, getConstant) } GET_REFLECTION_OBJECT_PTR(ce); - ZEND_HASH_FOREACH_VAL(&ce->constants_table, value) { - if (UNEXPECTED(zval_update_constant_ex(value, 1, ce) != SUCCESS)) { + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + if (UNEXPECTED(zval_update_constant_ex(&c->value, 1, ce) != SUCCESS)) { return; } } ZEND_HASH_FOREACH_END(); - if ((value = zend_hash_find(&ce->constants_table, name)) == NULL) { + if ((c = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) { RETURN_FALSE; } - ZVAL_DUP(return_value, value); + ZVAL_DUP(return_value, &c->value); +} +/* }}} */ + +/* {{{ proto public mixed ReflectionClass::getReflectionConstant(string name) + Returns the class' constant as ReflectionClassConstant objects */ +ZEND_METHOD(reflection_class, getReflectionConstant) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_class_constant *constant; + zend_string *name; + + GET_REFLECTION_OBJECT_PTR(ce); + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) { + return; + } + + if ((constant = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) { + RETURN_FALSE; + } + reflection_class_constant_factory(ce, name, constant, return_value); } /* }}} */ @@ -5171,6 +5447,14 @@ ZEND_METHOD(reflection_property, export) } /* }}} */ +/* {{{ proto public static mixed ReflectionClassConstant::export(mixed class, string name [, bool return]) throws ReflectionException + Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */ +ZEND_METHOD(reflection_class_constant, export) +{ + _reflection_export(INTERNAL_FUNCTION_PARAM_PASSTHRU, reflection_class_constant_ptr, 2); +} +/* }}} */ + /* {{{ proto public void ReflectionProperty::__construct(mixed class, string name) Constructor. Throws an Exception in case the given property does not exist */ ZEND_METHOD(reflection_property, __construct) @@ -6334,7 +6618,9 @@ static const zend_function_entry reflection_class_functions[] = { ZEND_ME(reflection_class, getProperties, arginfo_reflection_class_getProperties, 0) ZEND_ME(reflection_class, hasConstant, arginfo_reflection_class_hasConstant, 0) ZEND_ME(reflection_class, getConstants, arginfo_reflection__void, 0) + ZEND_ME(reflection_class, getReflectionConstants, arginfo_reflection__void, 0) ZEND_ME(reflection_class, getConstant, arginfo_reflection_class_getConstant, 0) + ZEND_ME(reflection_class, getReflectionConstant, arginfo_reflection_class_getConstant, 0) ZEND_ME(reflection_class, getInterfaces, arginfo_reflection__void, 0) ZEND_ME(reflection_class, getInterfaceNames, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isInterface, arginfo_reflection__void, 0) @@ -6426,6 +6712,33 @@ static const zend_function_entry reflection_property_functions[] = { PHP_FE_END }; +ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_class_constant_export, 0, 0, 2) + ZEND_ARG_INFO(0, class) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, return) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_class_constant___construct, 0, 0, 2) + ZEND_ARG_INFO(0, class) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +static const zend_function_entry reflection_class_constant_functions[] = { + ZEND_ME(reflection, __clone, arginfo_reflection__void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL) + ZEND_ME(reflection_class_constant, export, arginfo_reflection_class_constant_export, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) + ZEND_ME(reflection_class_constant, __construct, arginfo_reflection_class_constant___construct, 0) + ZEND_ME(reflection_class_constant, __toString, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, getName, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, getValue, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, isPublic, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, isPrivate, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, isProtected, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, getModifiers, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, getDeclaringClass, arginfo_reflection__void, 0) + ZEND_ME(reflection_class_constant, getDocComment, arginfo_reflection__void, 0) + PHP_FE_END +}; + ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_parameter_export, 0, 0, 2) ZEND_ARG_INFO(0, function) ZEND_ARG_INFO(0, parameter) @@ -6622,6 +6935,13 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ zend_declare_property_string(reflection_property_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); zend_declare_property_string(reflection_property_ptr, "class", sizeof("class")-1, "", ZEND_ACC_PUBLIC); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionClassConstant", reflection_class_constant_functions); + _reflection_entry.create_object = reflection_objects_new; + reflection_class_constant_ptr = zend_register_internal_class(&_reflection_entry); + zend_class_implements(reflection_class_constant_ptr, 1, reflector_ptr); + zend_declare_property_string(reflection_class_constant_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); + zend_declare_property_string(reflection_class_constant_ptr, "class", sizeof("class")-1, "", ZEND_ACC_PUBLIC); + REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_STATIC", ZEND_ACC_STATIC); REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_PUBLIC", ZEND_ACC_PUBLIC); REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_PROTECTED", ZEND_ACC_PROTECTED); diff --git a/ext/reflection/tests/017.phpt b/ext/reflection/tests/017.phpt index d40c4d83f9..1d9275d21b 100644 --- a/ext/reflection/tests/017.phpt +++ b/ext/reflection/tests/017.phpt @@ -10,12 +10,12 @@ class Foo { $class = new ReflectionClass("Foo"); echo $class; ?> ---EXPECTF-- +--EXPECTF-- Class [ class Foo ] { @@ %s017.php 2-4 - Constants [1] { - Constant [ string test ] { ok } + Constant [ public string test ] { ok } } - Static properties [0] { diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt new file mode 100644 index 0000000000..3d4f49f144 --- /dev/null +++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt @@ -0,0 +1,194 @@ +--TEST-- +Test usage of ReflectionClassConstant methods __toString(), export(), getName(), getValue(), isPublic(), isPrivate(), isProtected(), getModifiers(), getDeclaringClass() and getDocComment(). +--FILE-- +__toString()); + echo "export():\n"; + var_dump(ReflectionClassConstant::export($base, $constant, true)); + echo "export():\n"; + var_dump(ReflectionClassConstant::export($base, $constant, false)); + echo "getName():\n"; + var_dump($constInfo->getName()); + echo "getValue():\n"; + var_dump($constInfo->getValue()); + echo "isPublic():\n"; + var_dump($constInfo->isPublic()); + echo "isPrivate():\n"; + var_dump($constInfo->isPrivate()); + echo "isProtected():\n"; + var_dump($constInfo->isProtected()); + echo "getModifiers():\n"; + var_dump($constInfo->getModifiers()); + echo "getDeclaringClass():\n"; + var_dump($constInfo->getDeclaringClass()); + echo "getDocComment():\n"; + var_dump($constInfo->getDocComment()); + echo "\n**********************************\n"; +} + +class TestClass { + public const /** My Doc comment */ PUB = true; + /** Another doc comment */ + protected const PROT = 4; + private const PRIV = "keepOut"; +} +$instance = new TestClass(); + +reflectClassConstant("TestClass", "PUB"); +reflectClassConstant("TestClass", "PROT"); +reflectClassConstant("TestClass", "PRIV"); +reflectClassConstant($instance, "PRIV"); +reflectClassConstant($instance, "BAD_CONST"); + +?> +--EXPECTF-- +********************************** +Reflecting on class constant TestClass::PUB + +__toString(): +string(42) " Constant [ public boolean PUB ] { 1 } +" +export(): +string(42) " Constant [ public boolean PUB ] { 1 } +" +export(): + Constant [ public boolean PUB ] { 1 } + +NULL +getName(): +string(3) "PUB" +getValue(): +bool(true) +isPublic(): +bool(true) +isPrivate(): +bool(false) +isProtected(): +bool(false) +getModifiers(): +int(256) +getDeclaringClass(): +object(ReflectionClass)#3 (1) { + ["name"]=> + string(9) "TestClass" +} +getDocComment(): +string(21) "/** My Doc comment */" + +********************************** +********************************** +Reflecting on class constant TestClass::PROT + +__toString(): +string(46) " Constant [ protected integer PROT ] { 4 } +" +export(): +string(46) " Constant [ protected integer PROT ] { 4 } +" +export(): + Constant [ protected integer PROT ] { 4 } + +NULL +getName(): +string(4) "PROT" +getValue(): +int(4) +isPublic(): +bool(false) +isPrivate(): +bool(false) +isProtected(): +bool(true) +getModifiers(): +int(512) +getDeclaringClass(): +object(ReflectionClass)#3 (1) { + ["name"]=> + string(9) "TestClass" +} +getDocComment(): +string(26) "/** Another doc comment */" + +********************************** +********************************** +Reflecting on class constant TestClass::PRIV + +__toString(): +string(49) " Constant [ private string PRIV ] { keepOut } +" +export(): +string(49) " Constant [ private string PRIV ] { keepOut } +" +export(): + Constant [ private string PRIV ] { keepOut } + +NULL +getName(): +string(4) "PRIV" +getValue(): +string(7) "keepOut" +isPublic(): +bool(false) +isPrivate(): +bool(true) +isProtected(): +bool(false) +getModifiers(): +int(1024) +getDeclaringClass(): +object(ReflectionClass)#3 (1) { + ["name"]=> + string(9) "TestClass" +} +getDocComment(): +bool(false) + +********************************** +********************************** +Reflecting on class constant TestClass::PRIV + +__toString(): +string(49) " Constant [ private string PRIV ] { keepOut } +" +export(): +string(49) " Constant [ private string PRIV ] { keepOut } +" +export(): + Constant [ private string PRIV ] { keepOut } + +NULL +getName(): +string(4) "PRIV" +getValue(): +string(7) "keepOut" +isPublic(): +bool(false) +isPrivate(): +bool(true) +isProtected(): +bool(false) +getModifiers(): +int(1024) +getDeclaringClass(): +object(ReflectionClass)#3 (1) { + ["name"]=> + string(9) "TestClass" +} +getDocComment(): +bool(false) + +********************************** + +Fatal error: Uncaught ReflectionException: Class Constant TestClass::BAD_CONST does not exist in %s:%d +Stack trace: +#0 %s(%d): ReflectionClassConstant->__construct(Object(TestClass), 'BAD_CONST') +#1 %s(%d): reflectClassConstant(Object(TestClass), 'BAD_CONST') +#2 {main} + thrown in %s on line %d diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index b9a9b0d559..29d58420e3 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -12,9 +12,9 @@ echo $rc; Class [ class ReflectionClass implements Reflector ] { - Constants [3] { - Constant [ integer IS_IMPLICIT_ABSTRACT ] { 16 } - Constant [ integer IS_EXPLICIT_ABSTRACT ] { 32 } - Constant [ integer IS_FINAL ] { 4 } + Constant [ public integer IS_IMPLICIT_ABSTRACT ] { 16 } + Constant [ public integer IS_EXPLICIT_ABSTRACT ] { 32 } + Constant [ public integer IS_FINAL ] { 4 } } - Static properties [0] { @@ -34,7 +34,7 @@ Class [ class ReflectionClass implements Reflector ] { Property [ public $name ] } - - Methods [50] { + - Methods [52] { Method [ final private method __clone ] { - Parameters [0] { @@ -175,6 +175,12 @@ Class [ class ReflectionClass implements Reflector ] { } } + Method [ public method getReflectionConstants ] { + + - Parameters [0] { + } + } + Method [ public method getConstant ] { - Parameters [1] { @@ -182,6 +188,13 @@ Class [ class ReflectionClass implements Reflector ] { } } + Method [ public method getReflectionConstant ] { + + - Parameters [1] { + Parameter #0 [ $name ] + } + } + Method [ public method getInterfaces ] { - Parameters [0] { diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 4eda22a3f9..c1f9d99f2b 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -9,7 +9,7 @@ var_dump($ext->getClasses()); ?> ==DONE== --EXPECT-- -array(14) { +array(15) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -70,13 +70,18 @@ array(14) { ["name"]=> string(18) "ReflectionProperty" } - ["ReflectionExtension"]=> + ["ReflectionClassConstant"]=> object(ReflectionClass)#14 (1) { + ["name"]=> + string(23) "ReflectionClassConstant" + } + ["ReflectionExtension"]=> + object(ReflectionClass)#15 (1) { ["name"]=> string(19) "ReflectionExtension" } ["ReflectionZendExtension"]=> - object(ReflectionClass)#15 (1) { + object(ReflectionClass)#16 (1) { ["name"]=> string(23) "ReflectionZendExtension" } diff --git a/ext/reflection/tests/bug29986.phpt b/ext/reflection/tests/bug29986.phpt index 4c4d629f39..f5aa62a00b 100644 --- a/ext/reflection/tests/bug29986.phpt +++ b/ext/reflection/tests/bug29986.phpt @@ -20,11 +20,11 @@ Class [ class just_constants ] { @@ %s %d-%d - Constants [5] { - Constant [ boolean BOOLEAN_CONSTANT ] { 1 } - Constant [ null NULL_CONSTANT ] { } - Constant [ string STRING_CONSTANT ] { This is a string } - Constant [ integer INTEGER_CONSTANT ] { 1000 } - Constant [ float FLOAT_CONSTANT ] { 3.14159265 } + Constant [ public boolean BOOLEAN_CONSTANT ] { 1 } + Constant [ public null NULL_CONSTANT ] { } + Constant [ public string STRING_CONSTANT ] { This is a string } + Constant [ public integer INTEGER_CONSTANT ] { 1000 } + Constant [ public float FLOAT_CONSTANT ] { 3.14159265 } } - Static properties [0] { diff --git a/ext/reflection/tests/bug45765.phpt b/ext/reflection/tests/bug45765.phpt index b0c1be2c4c..7963a03eea 100644 --- a/ext/reflection/tests/bug45765.phpt +++ b/ext/reflection/tests/bug45765.phpt @@ -31,7 +31,7 @@ Object of class [ class foo extends foo2 ] { @@ %s 7-21 - Constants [1] { - Constant [ string BAR ] { foo's bar } + Constant [ public string BAR ] { foo's bar } } - Static properties [0] { diff --git a/tests/classes/constants_comments_001.phpt b/tests/classes/constants_comments_001.phpt new file mode 100644 index 0000000000..dbdd67c332 --- /dev/null +++ b/tests/classes/constants_comments_001.phpt @@ -0,0 +1,34 @@ +--TEST-- +Class constants and doc comments +--INI-- +opcache.save_comments=1 +--FILE-- +getReflectionConstants() as $rc) { + echo $rc->getName() . " : " . $rc->getDocComment() . "\n"; +} + + +?> +--EXPECT-- +Y1 : /** comment Y1 */ +Y2 : +Y3 : /** comment Y3 */ +X1 : /** comment X1 */ +X2 : +X3 : /** comment X3 */ diff --git a/tests/classes/constants_visibility_001.phpt b/tests/classes/constants_visibility_001.phpt new file mode 100644 index 0000000000..37a0154d92 --- /dev/null +++ b/tests/classes/constants_visibility_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +Class public constant visibility +--FILE-- +constDump(); + +?> +--EXPECTF-- +string(11) "publicConst" +string(11) "publicConst" +string(11) "publicConst" diff --git a/tests/classes/constants_visibility_002.phpt b/tests/classes/constants_visibility_002.phpt new file mode 100644 index 0000000000..82bdd6df1f --- /dev/null +++ b/tests/classes/constants_visibility_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +Class protected constant visibility +--FILE-- +constDump(); +constant('A::protectedConst'); + +?> +--EXPECTF-- +string(14) "protectedConst" +string(14) "protectedConst" + +Warning: constant(): Couldn't find constant A::protectedConst in %s on line 14 + +Fatal error: Uncaught Error: Cannot access protected const A::protectedConst in %s:14 +Stack trace: +#0 %s(14): constant('A::protectedCon...') +#1 {main} + thrown in %s on line 14 diff --git a/tests/classes/constants_visibility_003.phpt b/tests/classes/constants_visibility_003.phpt new file mode 100644 index 0000000000..7f049abffb --- /dev/null +++ b/tests/classes/constants_visibility_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +Class private constant visibility +--FILE-- +constDump(); +constant('A::privateConst'); + +?> +--EXPECTF-- +string(12) "privateConst" +string(12) "privateConst" + +Warning: constant(): Couldn't find constant A::privateConst in %s on line 14 + +Fatal error: Uncaught Error: Cannot access private const A::privateConst in %s:14 +Stack trace: +#0 %s(14): constant('A::privateConst') +#1 {main} + thrown in %s on line 14 diff --git a/tests/classes/constants_visibility_004.phpt b/tests/classes/constants_visibility_004.phpt new file mode 100644 index 0000000000..93acacf3c9 --- /dev/null +++ b/tests/classes/constants_visibility_004.phpt @@ -0,0 +1,28 @@ +--TEST-- +Only public and protected class constants should be inherited +--FILE-- + +--EXPECTF-- +int(1) +int(2) + +Fatal error: Uncaught Error: Undefined class constant 'Z' in %s:11 +Stack trace: +#0 %s(15): B::checkConstants() +#1 {main} + thrown in %s on line 11 diff --git a/tests/classes/constants_visibility_005.phpt b/tests/classes/constants_visibility_005.phpt new file mode 100644 index 0000000000..813009c675 --- /dev/null +++ b/tests/classes/constants_visibility_005.phpt @@ -0,0 +1,10 @@ +--TEST-- +Static constants are not allowed +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use 'static' as constant modifier in %s on line 3 diff --git a/tests/classes/constants_visibility_006.phpt b/tests/classes/constants_visibility_006.phpt new file mode 100644 index 0000000000..537c8eac0f --- /dev/null +++ b/tests/classes/constants_visibility_006.phpt @@ -0,0 +1,11 @@ +--TEST-- +Abstract constants are not allowed +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use 'abstract' as constant modifier in %s on line 3 + diff --git a/tests/classes/constants_visibility_007.phpt b/tests/classes/constants_visibility_007.phpt new file mode 100644 index 0000000000..f1b040c5c3 --- /dev/null +++ b/tests/classes/constants_visibility_007.phpt @@ -0,0 +1,10 @@ +--TEST-- +Final constants are not allowed +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use 'final' as constant modifier in %s on line 3 diff --git a/tests/classes/constants_visibility_error_001.phpt b/tests/classes/constants_visibility_error_001.phpt new file mode 100644 index 0000000000..397dd24882 --- /dev/null +++ b/tests/classes/constants_visibility_error_001.phpt @@ -0,0 +1,16 @@ +--TEST-- +Class private constant visibility error +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Cannot access private const A::privateConst in %s:6 +Stack trace: +#0 {main} + thrown in %s on line 6 diff --git a/tests/classes/constants_visibility_error_002.phpt b/tests/classes/constants_visibility_error_002.phpt new file mode 100644 index 0000000000..2980b52c37 --- /dev/null +++ b/tests/classes/constants_visibility_error_002.phpt @@ -0,0 +1,16 @@ +--TEST-- +Class protected constant visibility error +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Cannot access protected const A::protectedConst in %s:6 +Stack trace: +#0 {main} + thrown in %s on line 6 diff --git a/tests/classes/constants_visibility_error_003.phpt b/tests/classes/constants_visibility_error_003.phpt new file mode 100644 index 0000000000..c385bbd300 --- /dev/null +++ b/tests/classes/constants_visibility_error_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +A redeclared class constant must have the same or higher visibility +--FILE-- + +--EXPECT-- +Done \ No newline at end of file diff --git a/tests/classes/interface_constant_inheritance_006.phpt b/tests/classes/interface_constant_inheritance_006.phpt new file mode 100644 index 0000000000..125326b224 --- /dev/null +++ b/tests/classes/interface_constant_inheritance_006.phpt @@ -0,0 +1,10 @@ +--TEST-- +Ensure a interface can not have protected constants + +--FILE-- +