From 7cf5ff0466ecf6b5d109d9c9d36de2dc46e150ff Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 16 Aug 2024 15:13:46 +0200 Subject: [PATCH] Fix param with hooks but no visibility not treated as cpp Fixes GH-15438 --- NEWS | 2 ++ Zend/tests/property_hooks/gh15438_1.phpt | 20 ++++++++++++++++++++ Zend/tests/property_hooks/gh15438_2.phpt | 20 ++++++++++++++++++++ Zend/zend_compile.c | 21 ++++++++++++--------- 4 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/property_hooks/gh15438_1.phpt create mode 100644 Zend/tests/property_hooks/gh15438_2.phpt diff --git a/NEWS b/NEWS index cc863fac11928..e8762fa2419c8 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - Core: . Fixed bug GH-15408 (MSan false-positve on zend_max_execution_timer). (zeriyoshi) + . Fixed bug GH-15438 (Hooks on constructor promoted properties without + visibility are ignored). (ilutov) 15 Aug 2024, PHP 8.4.0beta3 diff --git a/Zend/tests/property_hooks/gh15438_1.phpt b/Zend/tests/property_hooks/gh15438_1.phpt new file mode 100644 index 0000000000000..2d06196f6b742 --- /dev/null +++ b/Zend/tests/property_hooks/gh15438_1.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-15438: Promoted properties with hooks but no visibility +--FILE-- + $value * 2; } + ) {} +} + +$c = new C(42); +var_dump($c); + +?> +--EXPECTF-- +object(C)#%d (1) { + ["prop"]=> + int(84) +} diff --git a/Zend/tests/property_hooks/gh15438_2.phpt b/Zend/tests/property_hooks/gh15438_2.phpt new file mode 100644 index 0000000000000..63175e5ecac70 --- /dev/null +++ b/Zend/tests/property_hooks/gh15438_2.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-15438: Untyped promoted, hooked properties with no default value default to null +--FILE-- + $value * 2; } + ) {} +} + +$c = new ReflectionClass(C::class)->newInstanceWithoutConstructor(); +var_dump($c); + +?> +--EXPECTF-- +object(C)#%d (1) { + ["prop"]=> + NULL +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9e736ea1b37ad..93c557df5a238 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7570,6 +7570,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_READONLY); + bool is_promoted = property_flags || hooks_ast; znode var_node, default_node; uint8_t opcode; @@ -7626,14 +7627,14 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 if (attributes_ast) { zend_compile_attributes( &op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER, - property_flags ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0 + is_promoted ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0 ); } bool forced_allow_nullable = false; if (type_ast) { uint32_t default_type = *default_ast_ptr ? Z_TYPE(default_node.u.constant) : IS_UNDEF; - bool force_nullable = default_type == IS_NULL && !property_flags; + bool force_nullable = default_type == IS_NULL && !is_promoted; op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; arg_info->type = zend_compile_typename_ex(type_ast, force_nullable, &forced_allow_nullable); @@ -7696,14 +7697,14 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 } uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0) - | (property_flags ? _ZEND_IS_PROMOTED_BIT : 0); + | (is_promoted ? _ZEND_IS_PROMOTED_BIT : 0); ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags; if (opcode == ZEND_RECV) { opline->op2.num = type_ast ? ZEND_TYPE_FULL_MASK(arg_info->type) : MAY_BE_ANY; } - if (property_flags) { + if (is_promoted) { zend_op_array *op_array = CG(active_op_array); zend_class_entry *scope = op_array->scope; @@ -7790,9 +7791,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 for (i = 0; i < list->children; i++) { zend_ast *param_ast = list->child[i]; + zend_ast *hooks_ast = param_ast->child[5]; bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; uint32_t flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_READONLY); - if (!flags) { + bool is_promoted = flags || hooks_ast; + if (!is_promoted) { continue; } @@ -8543,6 +8546,10 @@ static void zend_compile_property_hooks( /* Will be removed again, in case of Iterator or IteratorAggregate. */ ce->get_iterator = zend_hooked_object_get_iterator; } + + if (!prop_info->ce->parent_name) { + zend_verify_hooked_property(ce, prop_info, prop_name); + } } static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, zend_ast *attr_ast) /* {{{ */ @@ -8679,10 +8686,6 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f if (hooks_ast) { zend_compile_property_hooks(info, name, type_ast, zend_ast_get_list(hooks_ast)); - - if (!ce->parent_name) { - zend_verify_hooked_property(ce, info, name); - } } if (attr_ast) {