diff --git a/Zend/tests/array_unpack/classes.phpt b/Zend/tests/array_unpack/classes.phpt index fa4b0f8e536f4..a9b3cc204e484 100644 --- a/Zend/tests/array_unpack/classes.phpt +++ b/Zend/tests/array_unpack/classes.phpt @@ -44,4 +44,4 @@ array(3) { [2]=> int(3) } -Exception: Cannot declare self-referencing constant self::B +Exception: Cannot declare self-referencing constant D::A diff --git a/Zend/tests/bug41633_3.phpt b/Zend/tests/bug41633_3.phpt index 4f78194a7e7a1..b4de453ab4bd2 100644 --- a/Zend/tests/bug41633_3.phpt +++ b/Zend/tests/bug41633_3.phpt @@ -9,7 +9,7 @@ class Foo { echo Foo::A; ?> --EXPECTF-- -Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::B in %s:%d +Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::A in %s:%d Stack trace: #0 {main} thrown in %sbug41633_3.php on line %d diff --git a/Zend/tests/constant_expressions_self_referencing_array.phpt b/Zend/tests/constant_expressions_self_referencing_array.phpt index 214862071d74a..eb3a3e89800d8 100644 --- a/Zend/tests/constant_expressions_self_referencing_array.phpt +++ b/Zend/tests/constant_expressions_self_referencing_array.phpt @@ -9,7 +9,7 @@ class A { var_dump(A::FOO); ?> --EXPECTF-- -Fatal error: Uncaught Error: Cannot declare self-referencing constant self::BAR in %s:%d +Fatal error: Uncaught Error: Cannot declare self-referencing constant A::FOO in %s:%d Stack trace: #0 {main} thrown in %s on line %d diff --git a/Zend/tests/gh10709.phpt b/Zend/tests/gh10709.phpt new file mode 100644 index 0000000000000..3b802419a7186 --- /dev/null +++ b/Zend/tests/gh10709.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-10634: Fix recursive class constant evaluation +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot declare self-referencing constant B::HW diff --git a/Zend/zend_API.c b/Zend/zend_API.c index f59035be3f1b3..cfa0051123289 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1429,7 +1429,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } val = &c->value; - if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(val, name, c->ce) != SUCCESS)) { return FAILURE; } } diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 6ca402c61bf62..1f848120bc4dc 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -380,19 +380,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } if (ret_constant && Z_TYPE_P(ret_constant) == IS_CONSTANT_AST) { - zend_result ret; - - if (IS_CONSTANT_VISITED(ret_constant)) { - zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); - ret_constant = NULL; - goto failure; - } - - MARK_CONSTANT_VISITED(ret_constant); - ret = zval_update_constant_ex(ret_constant, c->ce); - RESET_CONSTANT_VISITED(ret_constant); - - if (UNEXPECTED(ret != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(ret_constant, constant_name, c->ce) != SUCCESS)) { ret_constant = NULL; goto failure; } @@ -401,6 +389,24 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * return ret_constant; } +ZEND_API zend_result zend_update_class_constant(zval *const_zv, zend_string *constant_name, zend_class_entry *ce) +{ + if (Z_TYPE_P(const_zv) != IS_CONSTANT_AST) { + return SUCCESS; + } + + if (IS_CONSTANT_VISITED(const_zv)) { + zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(constant_name)); + return FAILURE; + } + + MARK_CONSTANT_VISITED(const_zv); + zend_result result = zval_update_constant_ex(const_zv, ce); + RESET_CONSTANT_VISITED(const_zv); + + return result; +} + ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags) { zend_constant *c; diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 736dbb0091085..0b25b181a04e2 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -78,6 +78,7 @@ 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); ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *constant_name, zend_class_entry *scope, uint32_t flags); +ZEND_API zend_result zend_update_class_constant(zval *const_zv, zend_string *constant_name, zend_class_entry *ce); ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number); ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number); ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 4845a92b95a7e..63da47c85d6c5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5914,7 +5914,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } value = &c->value; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); + zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce); if (UNEXPECTED(EG(exception) != NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a5a5eabf2b037..1f00571d64047 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7089,7 +7089,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } value = &c->value; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); + zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce); if (UNEXPECTED(EG(exception) != NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -24597,7 +24597,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } value = &c->value; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); + zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce); if (UNEXPECTED(EG(exception) != NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -33382,7 +33382,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } value = &c->value; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - zval_update_constant_ex(value, c->ce); + zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce); if (UNEXPECTED(EG(exception) != NULL)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index a912b5e604bb5..2adddf5bdb17b 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3742,10 +3742,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) do { ok = 1; changed = 0; - ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + zend_string *const_name; + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, const_name, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) { + if (EXPECTED(zend_update_class_constant(val, const_name, c->ce) == SUCCESS)) { was_changed = changed = 1; } else { ok = 0; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9a9685e7292e7..bb5d4c7fa7a53 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -301,7 +301,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{ static void _const_string(smart_str *str, char *name, zval *value, char *indent); static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, char* indent); static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, char* indent); -static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char* indent); +static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char *indent); static void _extension_string(smart_str *str, zend_module_entry *module, char *indent); static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent); @@ -385,7 +385,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char zend_class_constant *c; ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) { - _class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent)); + _class_const_string(str, key, c, ZSTR_VAL(sub_indent)); if (UNEXPECTED(EG(exception))) { zend_string_release(sub_indent); return; @@ -557,9 +557,9 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent) /* }}} */ /* {{{ _class_const_string */ -static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent) +static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char *indent) { - if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) { + if (zend_update_class_constant(&c->value, name, c->ce) == FAILURE) { return; } @@ -567,7 +567,7 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : ""; const char *type = zend_zval_type_name(&c->value); smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", - indent, final, visibility, type, name); + indent, final, visibility, type, ZSTR_VAL(name)); if (Z_TYPE(c->value) == IS_ARRAY) { smart_str_appends(str, "Array"); } else if (Z_TYPE(c->value) == IS_OBJECT) { @@ -3780,7 +3780,7 @@ ZEND_METHOD(ReflectionClassConstant, __toString) ZVAL_DEREF(name); ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING); - _class_const_string(&str, Z_STRVAL_P(name), ref, ""); + _class_const_string(&str, Z_STR_P(name), ref, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ @@ -3872,7 +3872,10 @@ ZEND_METHOD(ReflectionClassConstant, getValue) GET_REFLECTION_OBJECT_PTR(ref); if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { - zval_update_constant_ex(&ref->value, ref->ce); + zval *name = reflection_prop_name(ZEND_THIS); + // FIXME: Can this be IS_UNDEF? + ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING); + zend_update_class_constant(&ref->value, Z_STR_P(name), ref->ce); } ZVAL_COPY_OR_DUP(return_value, &ref->value); } @@ -4678,7 +4681,7 @@ ZEND_METHOD(ReflectionClass, getConstants) array_init(return_value); ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { - if (UNEXPECTED(zval_update_constant_ex(&constant->value, constant->ce) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(&constant->value, key, constant->ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4736,8 +4739,9 @@ ZEND_METHOD(ReflectionClass, getConstant) GET_REFLECTION_OBJECT_PTR(ce); constants_table = CE_CONSTANTS_TABLE(ce); - ZEND_HASH_FOREACH_PTR(constants_table, c) { - if (UNEXPECTED(zval_update_constant_ex(&c->value, c->ce) != SUCCESS)) { + zend_string *const_name; + ZEND_HASH_FOREACH_STR_KEY_PTR(constants_table, const_name, c) { + if (UNEXPECTED(zend_update_class_constant(&c->value, const_name, c->ce) != SUCCESS)) { RETURN_THROWS(); } } ZEND_HASH_FOREACH_END(); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 1e50a37f2c687..bbde91c9ef878 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -596,7 +596,7 @@ PHP_FUNCTION(constant) ZVAL_COPY_OR_DUP(return_value, c); if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) { - if (UNEXPECTED(zval_update_constant_ex(return_value, scope) != SUCCESS)) { + if (UNEXPECTED(zend_update_class_constant(return_value, const_name, scope) != SUCCESS)) { RETURN_THROWS(); } }