Skip to content

Commit 5d9c155

Browse files
committed
Fix inheritance of hooks onto plain properties
1 parent 43f688e commit 5d9c155

File tree

3 files changed

+34
-34
lines changed

3 files changed

+34
-34
lines changed

Zend/tests/property_hooks/interface_get_set_readonly.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class C implements I {
1010
}
1111
?>
1212
--EXPECTF--
13-
Fatal error: Readonly property C::$prop does not satisfy abstract read-write property I::$prop in %s on line %d
13+
Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (I::$prop::set) in %s on line %d

Zend/tests/property_hooks/override_by_plain_prop.phpt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Hooks must not be overridden by a plain property
2+
Hooks are inherited to plain property
33
--FILE--
44
<?php
55

@@ -27,4 +27,6 @@ var_dump($b->prop);
2727
A::$prop::set
2828
A::$prop::get
2929
int(42)
30-
int(43)
30+
A::$prop::set
31+
A::$prop::get
32+
int(42)

Zend/zend_inheritance.c

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,18 +1284,26 @@ static void emit_incompatible_property_error(
12841284
ZSTR_VAL(parent->ce->name));
12851285
}
12861286

1287+
static bool property_has_operation(zend_property_info *prop_info, zend_property_hook_kind kind)
1288+
{
1289+
return (!(prop_info->flags & ZEND_ACC_VIRTUAL)
1290+
&& (kind == ZEND_PROPERTY_HOOK_GET || !(prop_info->flags & ZEND_ACC_READONLY)))
1291+
|| (prop_info->hooks && prop_info->hooks[kind]);
1292+
}
1293+
12871294
static void inherit_property_hook(
12881295
zend_class_entry *ce,
1289-
zend_property_info *parent_info, zend_function **parent_ptr,
1290-
zend_property_info *child_info, zend_function **child_ptr
1296+
zend_property_info *parent_info,
1297+
zend_property_info *child_info,
1298+
zend_property_hook_kind kind
12911299
) {
1292-
zend_function *parent = *parent_ptr;
1293-
zend_function *child = *child_ptr;
1300+
zend_function *parent = parent_info->hooks ? parent_info->hooks[kind] : NULL;
1301+
zend_function *child = child_info->hooks ? child_info->hooks[kind] : NULL;
12941302

1295-
if (child && (child->common.fn_flags & ZEND_ACC_OVERRIDE)) {
1296-
if (!(parent_info->flags & ZEND_ACC_VIRTUAL) || parent) {
1297-
child->common.fn_flags &= ~ZEND_ACC_OVERRIDE;
1298-
}
1303+
if (child
1304+
&& (child->common.fn_flags & ZEND_ACC_OVERRIDE)
1305+
&& property_has_operation(parent_info, kind)) {
1306+
child->common.fn_flags &= ~ZEND_ACC_OVERRIDE;
12991307
}
13001308

13011309
if (!parent) {
@@ -1304,10 +1312,18 @@ static void inherit_property_hook(
13041312

13051313
if (!child) {
13061314
if (parent->common.fn_flags & ZEND_ACC_ABSTRACT) {
1315+
/* Backed properties are considered to always implement get, and set when they are not readonly. */
1316+
if (property_has_operation(child_info, kind)) {
1317+
return;
1318+
}
13071319
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
13081320
}
1309-
1310-
*child_ptr = zend_duplicate_function(parent, ce);
1321+
if (!child_info->hooks) {
1322+
ce->num_hooked_props++;
1323+
child_info->hooks = zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1324+
memset(child_info->hooks, 0, ZEND_PROPERTY_HOOK_STRUCT_SIZE);
1325+
}
1326+
child_info->hooks[kind] = zend_duplicate_function(parent, ce);
13111327
return;
13121328
}
13131329

@@ -1375,17 +1391,7 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
13751391
(child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ZSTR_VAL(ce->name), ZSTR_VAL(key));
13761392
}
13771393
if (UNEXPECTED((child_info->flags & ZEND_ACC_READONLY) != (parent_info->flags & ZEND_ACC_READONLY))) {
1378-
/* If the parent property is abstract, the child may be readonly if the parent only requires read. */
1379-
if (parent_info->flags & ZEND_ACC_ABSTRACT) {
1380-
ZEND_ASSERT(!(parent_info->flags & ZEND_ACC_READONLY));
1381-
ZEND_ASSERT(parent_info->hooks);
1382-
if (UNEXPECTED(parent_info->hooks[ZEND_PROPERTY_HOOK_SET])) {
1383-
zend_error_noreturn(E_COMPILE_ERROR,
1384-
"Readonly property %s::$%s does not satisfy abstract read-write property %s::$%s",
1385-
ZSTR_VAL(ce->name), ZSTR_VAL(key),
1386-
ZSTR_VAL(parent_info->ce->name), ZSTR_VAL(key));
1387-
}
1388-
} else {
1394+
if (!(parent_info->flags & ZEND_ACC_ABSTRACT)) {
13891395
zend_error_noreturn(E_COMPILE_ERROR,
13901396
"Cannot redeclare %s property %s::$%s as %s %s::$%s",
13911397
parent_info->flags & ZEND_ACC_READONLY ? "readonly" : "non-readonly",
@@ -1413,18 +1419,10 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
14131419
child_info->flags &= ~ZEND_ACC_VIRTUAL;
14141420
}
14151421

1416-
zend_function **parent_hooks = parent_info->hooks;
1417-
zend_function **child_hooks = child_info->hooks;
1418-
if (child_hooks) {
1422+
if (parent_info->hooks || child_info->hooks) {
14191423
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
1420-
if (parent_hooks) {
1421-
inherit_property_hook(ce, parent_info, &parent_hooks[i], child_info, &child_hooks[i]);
1422-
} else if (child_hooks[i]) {
1423-
child_hooks[i]->common.fn_flags &= ~ZEND_ACC_OVERRIDE;
1424-
}
1424+
inherit_property_hook(ce, parent_info, child_info, i);
14251425
}
1426-
} else if (parent_hooks) {
1427-
ce->num_hooked_props++;
14281426
}
14291427

14301428
prop_variance variance = prop_get_variance(parent_info);

0 commit comments

Comments
 (0)