summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-12-08 12:40:42 +0300
committerDmitry Stogov <dmitry@zend.com>2015-12-08 12:40:42 +0300
commita75c195000b3226904103244fa9c3d0ce1111838 (patch)
tree1242c6dc6078647c43a295e4c83d8b6d00e88324
parenta8b7d0c29d13902f748c08d8a58e3d304450a1a2 (diff)
downloadphp-git-a75c195000b3226904103244fa9c3d0ce1111838.tar.gz
Implemented the RFC `Support Class Constant Visibility`.
Squashed commit of the following: commit f11ca0e7a57793fa0e3e7f6d451720e6c42bb0b9 Author: Dmitry Stogov <dmitry@zend.com> Date: Tue Dec 8 12:38:42 2015 +0300 Fixed test expectation commit 211f873f542504d0a0f72b6b5cb23908a1c99a2d Author: Dmitry Stogov <dmitry@zend.com> 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 <dmitry@zend.com> Date: Mon Dec 7 11:18:55 2015 +0300 Fixed issues found by Nikita commit 544dbd5b47e40d38a8ccb96bc5583e9cb7fdd723 Author: Dmitry Stogov <dmitry@zend.com> 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.
-rw-r--r--Zend/zend_API.c42
-rw-r--r--Zend/zend_API.h1
-rw-r--r--Zend/zend_ast.h2
-rw-r--r--Zend/zend_compile.c41
-rw-r--r--Zend/zend_compile.h6
-rw-r--r--Zend/zend_constants.c27
-rw-r--r--Zend/zend_constants.h1
-rw-r--r--Zend/zend_inheritance.c74
-rw-r--r--Zend/zend_language_parser.y8
-rw-r--r--Zend/zend_opcode.c24
-rw-r--r--Zend/zend_types.h4
-rw-r--r--Zend/zend_vm_def.h11
-rw-r--r--Zend/zend_vm_execute.h49
-rw-r--r--ext/opcache/Optimizer/pass1_5.c8
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c11
-rw-r--r--ext/opcache/zend_file_cache.c46
-rw-r--r--ext/opcache/zend_persist.c35
-rw-r--r--ext/opcache/zend_persist_calc.c17
-rw-r--r--ext/reflection/php_reflection.c348
-rw-r--r--ext/reflection/tests/017.phpt4
-rw-r--r--ext/reflection/tests/ReflectionClassConstant_basic1.phpt194
-rw-r--r--ext/reflection/tests/ReflectionClass_toString_001.phpt21
-rw-r--r--ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt11
-rw-r--r--ext/reflection/tests/bug29986.phpt10
-rw-r--r--ext/reflection/tests/bug45765.phpt2
-rw-r--r--tests/classes/constants_comments_001.phpt34
-rw-r--r--tests/classes/constants_visibility_001.phpt23
-rw-r--r--tests/classes/constants_visibility_002.phpt30
-rw-r--r--tests/classes/constants_visibility_003.phpt30
-rw-r--r--tests/classes/constants_visibility_004.phpt28
-rw-r--r--tests/classes/constants_visibility_005.phpt10
-rw-r--r--tests/classes/constants_visibility_006.phpt11
-rw-r--r--tests/classes/constants_visibility_007.phpt10
-rw-r--r--tests/classes/constants_visibility_error_001.phpt16
-rw-r--r--tests/classes/constants_visibility_error_002.phpt16
-rw-r--r--tests/classes/constants_visibility_error_003.phpt16
-rw-r--r--tests/classes/constants_visibility_error_004.phpt16
-rw-r--r--tests/classes/interface_constant_inheritance_005.phpt12
-rw-r--r--tests/classes/interface_constant_inheritance_006.phpt10
-rw-r--r--tests/classes/interface_constant_inheritance_007.phpt10
40 files changed, 1143 insertions, 126 deletions
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 [ <user> 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--
+<?php
+
+function reflectClassConstant($base, $constant) {
+ $constInfo = new ReflectionClassConstant($base, $constant);
+ echo "**********************************\n";
+ $class = is_object($base) ? get_class($base) : $base;
+ echo "Reflecting on class constant $class::$constant\n\n";
+ echo "__toString():\n";
+ var_dump($constInfo->__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 [ <internal:Reflection> 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 [ <internal:Reflection> class ReflectionClass implements Reflector ] {
Property [ <default> public $name ]
}
- - Methods [50] {
+ - Methods [52] {
Method [ <internal:Reflection> final private method __clone ] {
- Parameters [0] {
@@ -175,6 +175,12 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
+ Method [ <internal:Reflection> public method getReflectionConstants ] {
+
+ - Parameters [0] {
+ }
+ }
+
Method [ <internal:Reflection> public method getConstant ] {
- Parameters [1] {
@@ -182,6 +188,13 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
+ Method [ <internal:Reflection> public method getReflectionConstant ] {
+
+ - Parameters [1] {
+ Parameter #0 [ <required> $name ]
+ }
+ }
+
Method [ <internal:Reflection> 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 [ <user> 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 [ <user> 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--
+<?php
+class X {
+ /** comment X1 */
+ const X1 = 1;
+ const X2 = 2;
+ /** comment X3 */
+ const X3 = 3;
+}
+class Y extends X {
+ /** comment Y1 */
+ const Y1 = 1;
+ const Y2 = 2;
+ /** comment Y3 */
+ const Y3 = 3;
+}
+$r = new ReflectionClass('Y');
+foreach ($r->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--
+<?php
+class A {
+ public const publicConst = 'publicConst';
+ static function staticConstDump() {
+ var_dump(self::publicConst);
+ }
+ function constDump() {
+ var_dump(self::publicConst);
+ }
+}
+
+var_dump(A::publicConst);
+A::staticConstDump();
+(new A())->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--
+<?php
+class A {
+ protected const protectedConst = 'protectedConst';
+ static function staticConstDump() {
+ var_dump(self::protectedConst);
+ }
+ function constDump() {
+ var_dump(self::protectedConst);
+ }
+}
+
+A::staticConstDump();
+(new A())->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--
+<?php
+class A {
+ private const privateConst = 'privateConst';
+ static function staticConstDump() {
+ var_dump(self::privateConst);
+ }
+ function constDump() {
+ var_dump(self::privateConst);
+ }
+}
+
+A::staticConstDump();
+(new A())->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--
+<?php
+class A {
+ public const X = 1;
+ protected const Y = 2;
+ private const Z = 3;
+}
+class B extends A {
+ static public function checkConstants() {
+ var_dump(self::X);
+ var_dump(self::Y);
+ var_dump(self::Z);
+ }
+}
+
+B::checkConstants();
+?>
+--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--
+<?php
+class A {
+ static const X = 1;
+}
+?>
+--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--
+<?php
+class A {
+ abstract const X = 1;
+}
+?>
+--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--
+<?php
+class A {
+ final const X = 1;
+}
+?>
+--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--
+<?php
+class A {
+ private const privateConst = 'privateConst';
+}
+
+var_dump(A::privateConst);
+
+?>
+--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--
+<?php
+class A {
+ protected const protectedConst = 'protectedConst';
+}
+
+var_dump(A::protectedConst);
+
+?>
+--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--
+<?php
+
+class A {
+ public const publicConst = 0;
+}
+
+class B extends A {
+ protected const publicConst = 1;
+}
+
+
+--EXPECTF--
+Fatal error: Access level to B::publicConst must be public (as in class A) in %s on line 9
diff --git a/tests/classes/constants_visibility_error_004.phpt b/tests/classes/constants_visibility_error_004.phpt
new file mode 100644
index 0000000000..fe37b0691f
--- /dev/null
+++ b/tests/classes/constants_visibility_error_004.phpt
@@ -0,0 +1,16 @@
+--TEST--
+A redeclared class constant must have the same or higher visibility
+--FILE--
+<?php
+
+class A {
+ protected const protectedConst = 0;
+}
+
+class B extends A {
+ private const protectedConst = 1;
+}
+
+
+--EXPECTF--
+Fatal error: Access level to B::protectedConst must be protected (as in class A) or weaker in %s on line 9
diff --git a/tests/classes/interface_constant_inheritance_005.phpt b/tests/classes/interface_constant_inheritance_005.phpt
new file mode 100644
index 0000000000..60bf222e85
--- /dev/null
+++ b/tests/classes/interface_constant_inheritance_005.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Ensure a interface can have public constants
+--FILE--
+<?php
+interface IA {
+ public const FOO = 10;
+}
+
+echo "Done\n";
+?>
+--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--
+<?php
+interface A {
+ protected const FOO = 10;
+}
+--EXPECTF--
+Fatal error: Access type for interface constant A::FOO must be public in %s on line 3
diff --git a/tests/classes/interface_constant_inheritance_007.phpt b/tests/classes/interface_constant_inheritance_007.phpt
new file mode 100644
index 0000000000..52695343e1
--- /dev/null
+++ b/tests/classes/interface_constant_inheritance_007.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Ensure a interface can not have private constants
+
+--FILE--
+<?php
+interface A {
+ private const FOO = 10;
+}
+--EXPECTF--
+Fatal error: Access type for interface constant A::FOO must be public in %s on line 3