diff --git a/Zend/tests/property_hooks/gh25264.phpt b/Zend/tests/property_hooks/gh25264.phpt new file mode 100644 index 000000000000..4f9f51b75e61 --- /dev/null +++ b/Zend/tests/property_hooks/gh25264.phpt @@ -0,0 +1,53 @@ +--TEST-- +GH-25264: Implementing an interface must not modify inherited property hooks +--FILE-- + 1; } +} + +class Q { + public $prop { get => 2; set {} } +} + +interface I { + public $prop { get; set; } +} + +interface J { + public $prop { get; } +} + +abstract class A extends P implements I {} + +class B extends Q implements I {} + +abstract class C implements J, I {} + +class D implements J { + public $prop { get => 3; } +} + +var_dump((new ReflectionProperty(P::class, 'prop'))->hasHook(PropertyHookType::Set)); +var_dump((new ReflectionProperty(J::class, 'prop'))->hasHook(PropertyHookType::Set)); +var_dump((new ReflectionProperty(A::class, 'prop'))->hasHook(PropertyHookType::Set)); + +$p = new P; +try { + $p->prop = 4; +} catch (Error $e) { + echo $e::class, "\n"; +} + +var_dump((new B)->prop); +var_dump((new D)->prop); + +?> +--EXPECT-- +bool(false) +bool(false) +bool(true) +Error +int(2) +int(3) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 4d73dd003a31..347d007578fb 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1347,12 +1347,36 @@ static bool property_has_operation(zend_property_info *prop_info, zend_property_ || (prop_info->hooks && prop_info->hooks[kind]); } +static zend_property_info *separate_property_info(const zend_property_info *prop_info, zend_class_entry *ce) +{ + zend_property_info *new_prop_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info)); + memcpy(new_prop_info, prop_info, sizeof(zend_property_info)); + new_prop_info->ce = ce; + zend_string_addref(new_prop_info->name); + if (new_prop_info->doc_comment) { + zend_string_addref(new_prop_info->doc_comment); + } + if (new_prop_info->attributes && !(GC_FLAGS(new_prop_info->attributes) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(new_prop_info->attributes); + } + zend_type_copy_ctor(&new_prop_info->type, /* use_arena */ true, /* persistent */ false); + if (new_prop_info->hooks) { + new_prop_info->hooks = zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE); + for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { + zend_function *hook = prop_info->hooks[i]; + new_prop_info->hooks[i] = hook ? zend_duplicate_function(hook, ce) : NULL; + } + } + return new_prop_info; +} + static void inherit_property_hook( zend_class_entry *ce, zend_property_info *parent_info, - zend_property_info *child_info, + zval *child_zv, zend_property_hook_kind kind ) { + zend_property_info *child_info = Z_PTR_P(child_zv); zend_function *parent = parent_info->hooks ? parent_info->hooks[kind] : NULL; zend_function *child = child_info->hooks ? child_info->hooks[kind] : NULL; @@ -1374,6 +1398,10 @@ static void inherit_property_hook( } ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; } + if (child_info->ce != ce) { + child_info = separate_property_info(child_info, ce); + Z_PTR_P(child_zv) = child_info; + } if (!child_info->hooks) { ce->num_hooked_props++; child_info->hooks = zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE); @@ -1383,6 +1411,13 @@ static void inherit_property_hook( return; } + if (child->common.scope != ce) { + do_inheritance_check_on_method( + child, child->common.scope, parent, parent->common.scope, ce, /* child */ NULL, + ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY); + return; + } + child->common.prototype = parent->common.prototype ? parent->common.prototype : parent; uint32_t parent_flags = parent->common.fn_flags; @@ -1522,8 +1557,9 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke if (parent_info->hooks || child_info->hooks) { for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { - inherit_property_hook(ce, parent_info, child_info, i); + inherit_property_hook(ce, parent_info, child, i); } + child_info = Z_PTR_P(child); } prop_variance variance = prop_get_variance(parent_info);