diff --git a/Zend/tests/attributes/override/properties_01.phpt b/Zend/tests/attributes/override/properties_01.phpt new file mode 100644 index 0000000000000..d83f935f83496 --- /dev/null +++ b/Zend/tests/attributes/override/properties_01.phpt @@ -0,0 +1,39 @@ +--TEST-- +#[\Override]: Properties +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_02.phpt b/Zend/tests/attributes/override/properties_02.phpt new file mode 100644 index 0000000000000..3791ee2fe5a2b --- /dev/null +++ b/Zend/tests/attributes/override/properties_02.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\Override]: Properties: No parent class. +--FILE-- + +--EXPECTF-- +Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_03.phpt b/Zend/tests/attributes/override/properties_03.phpt new file mode 100644 index 0000000000000..c2b2ea0acc2e0 --- /dev/null +++ b/Zend/tests/attributes/override/properties_03.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Properties: No parent class, but child implements matching interface. +--FILE-- + +--EXPECTF-- +Fatal error: P::$i has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_04.phpt b/Zend/tests/attributes/override/properties_04.phpt new file mode 100644 index 0000000000000..142a1d0c640ac --- /dev/null +++ b/Zend/tests/attributes/override/properties_04.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Properties: No parent class, but child implements matching interface (2). +--FILE-- + +--EXPECTF-- +Fatal error: P::$i has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_05.phpt b/Zend/tests/attributes/override/properties_05.phpt new file mode 100644 index 0000000000000..71113dad60e43 --- /dev/null +++ b/Zend/tests/attributes/override/properties_05.phpt @@ -0,0 +1,26 @@ +--TEST-- +#[\Override]: Properties: No parent interface. +--FILE-- + +--EXPECTF-- +Fatal error: I::$i has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_06.phpt b/Zend/tests/attributes/override/properties_06.phpt new file mode 100644 index 0000000000000..66309ac625c0c --- /dev/null +++ b/Zend/tests/attributes/override/properties_06.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Properties: On trait. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_07.phpt b/Zend/tests/attributes/override/properties_07.phpt new file mode 100644 index 0000000000000..e09cfe3a46a5f --- /dev/null +++ b/Zend/tests/attributes/override/properties_07.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: On used trait without parent method. +--FILE-- + +--EXPECTF-- +Fatal error: Foo::$t has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_08.phpt b/Zend/tests/attributes/override/properties_08.phpt new file mode 100644 index 0000000000000..fb3cd508c4249 --- /dev/null +++ b/Zend/tests/attributes/override/properties_08.phpt @@ -0,0 +1,23 @@ +--TEST-- +#[\Override]: Properties: On used trait with interface method. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_09.phpt b/Zend/tests/attributes/override/properties_09.phpt new file mode 100644 index 0000000000000..05fbb4c83ed81 --- /dev/null +++ b/Zend/tests/attributes/override/properties_09.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: Parent property is private. +--FILE-- + +--EXPECTF-- +Fatal error: C::$p has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_10.phpt b/Zend/tests/attributes/override/properties_10.phpt new file mode 100644 index 0000000000000..918d248748e27 --- /dev/null +++ b/Zend/tests/attributes/override/properties_10.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: Parent property is private (2). +--FILE-- + +--EXPECTF-- +Fatal error: C::$p has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_11.phpt b/Zend/tests/attributes/override/properties_11.phpt new file mode 100644 index 0000000000000..0eeedd4f86060 --- /dev/null +++ b/Zend/tests/attributes/override/properties_11.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: Parent property is protected. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_12.phpt b/Zend/tests/attributes/override/properties_12.phpt new file mode 100644 index 0000000000000..94d35454f789b --- /dev/null +++ b/Zend/tests/attributes/override/properties_12.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: Parent property is protected (2). +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_13.phpt b/Zend/tests/attributes/override/properties_13.phpt new file mode 100644 index 0000000000000..d9ab0a6b9a299 --- /dev/null +++ b/Zend/tests/attributes/override/properties_13.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Properties: Redeclared trait property. +--FILE-- + +--EXPECTF-- +Fatal error: C::$t has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_14.phpt b/Zend/tests/attributes/override/properties_14.phpt new file mode 100644 index 0000000000000..4e098c6d7212c --- /dev/null +++ b/Zend/tests/attributes/override/properties_14.phpt @@ -0,0 +1,25 @@ +--TEST-- +#[\Override]: Properties: Redeclared trait property with interface. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_15.phpt b/Zend/tests/attributes/override/properties_15.phpt new file mode 100644 index 0000000000000..37d46b2a170a3 --- /dev/null +++ b/Zend/tests/attributes/override/properties_15.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: Valid anonymous class. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_16.phpt b/Zend/tests/attributes/override/properties_16.phpt new file mode 100644 index 0000000000000..57ea17818c406 --- /dev/null +++ b/Zend/tests/attributes/override/properties_16.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Properties: Invalid anonymous class. +--FILE-- + +--EXPECTF-- +Fatal error: I@anonymous::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_17.phpt b/Zend/tests/attributes/override/properties_17.phpt new file mode 100644 index 0000000000000..389d66035ea44 --- /dev/null +++ b/Zend/tests/attributes/override/properties_17.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\Override]: Properties: Static property no parent class. +--FILE-- + +--EXPECTF-- +Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/tests/attributes/override/properties_18.phpt b/Zend/tests/attributes/override/properties_18.phpt new file mode 100644 index 0000000000000..66731a69f8e8a --- /dev/null +++ b/Zend/tests/attributes/override/properties_18.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Properties: Static property. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_19.phpt b/Zend/tests/attributes/override/properties_19.phpt new file mode 100644 index 0000000000000..1844664e3ca27 --- /dev/null +++ b/Zend/tests/attributes/override/properties_19.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Properties: valid promoted property +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/properties_20.phpt b/Zend/tests/attributes/override/properties_20.phpt new file mode 100644 index 0000000000000..fcfbfa727c849 --- /dev/null +++ b/Zend/tests/attributes/override/properties_20.phpt @@ -0,0 +1,17 @@ +--TEST-- +#[\Override]: Properties: invalid promoted property +--FILE-- + +--EXPECTF-- +Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index fe70de83e4d21..242b751160864 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -68,7 +68,7 @@ public function __debugInfo(): array {} /** * @strict-properties */ -#[Attribute(Attribute::TARGET_METHOD)] +#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY)] final class Override { public function __construct() {} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 14afe40c01adf..b79231f98d323 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9aee3d8f2ced376f5929048444eaa2529ff90311 */ + * Stub hash: 715016d1ff1b0a6abb325a71083eff397a080c44 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -239,7 +239,7 @@ static zend_class_entry *register_class_Override(void) zend_attribute *attribute_Attribute_class_Override_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Override_0, 1); zend_string_release(attribute_name_Attribute_class_Override_0); zval attribute_Attribute_class_Override_0_arg0; - ZVAL_LONG(&attribute_Attribute_class_Override_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD); + ZVAL_LONG(&attribute_Attribute_class_Override_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY); ZVAL_COPY_VALUE(&attribute_Attribute_class_Override_0->args[0].value, &attribute_Attribute_class_Override_0_arg0); return class_entry; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f3f6d1b75aec1..fe62b4c1d7a2b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7881,6 +7881,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 zend_compile_attributes( &prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER); } + + zend_attribute *override_attribute = zend_get_attribute_str(prop->attributes, "override", sizeof("override")-1); + if (override_attribute) { + prop->flags |= ZEND_ACC_OVERRIDE; + } } } @@ -8859,6 +8864,11 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0); } + zend_attribute *override_attribute = zend_get_attribute_str(info->attributes, "override", sizeof("override")-1); + if (override_attribute) { + info->flags |= ZEND_ACC_OVERRIDE; + } + CG(context).active_property_info_name = old_active_property_info_name; } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 7ac0a2b8b2c44..c663c34d91ddc 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -253,7 +253,10 @@ typedef struct _zend_oparray_context { /* or IS_CONSTANT_VISITED_MARK | | | */ #define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */ /* | | | */ -/* Property Flags (unused: 13...) | | | */ +/* has #[\Override] attribute | | | */ +#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | X | */ +/* | | | */ +/* Property Flags (unused: 13-27,29...) | | | */ /* =========== | | | */ /* | | | */ /* Promoted property / parameter | | | */ @@ -392,9 +395,6 @@ typedef struct _zend_oparray_context { /* supports opcache compile-time evaluation (funcs) | | | */ #define ZEND_ACC_COMPILE_TIME_EVAL (1 << 27) /* | X | | */ /* | | | */ -/* has #[\Override] attribute | | | */ -#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | | */ -/* | | | */ /* Has IS_PTR operands that needs special cleaning; same | | | */ /* value as ZEND_ACC_OVERRIDE but override is for class | | | */ /* methods and this is for the top level op array | | | */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d27cca5b76187..155ec084a462c 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1566,6 +1566,10 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke ZSTR_VAL(key), ZSTR_VAL(parent_info->ce->name)); } + + if (child_info->flags & ZEND_ACC_OVERRIDE) { + child_info->flags &= ~ZEND_ACC_OVERRIDE; + } } } else { zend_function **hooks = parent_info->hooks; @@ -2304,6 +2308,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry void zend_inheritance_check_override(const zend_class_entry *ce) { zend_function *f; + zend_property_info *p; if (ce->ce_flags & ZEND_ACC_TRAIT) { return; @@ -2320,6 +2325,15 @@ void zend_inheritance_check_override(const zend_class_entry *ce) } } ZEND_HASH_FOREACH_END(); + ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, p) { + if (p->flags & ZEND_ACC_OVERRIDE) { + zend_error_noreturn( + E_COMPILE_ERROR, + "%s::$%s has #[\\Override] attribute, but no matching parent property exists", + ZSTR_VAL(ce->name), zend_get_unmangled_property_name(p->name)); + } + } ZEND_HASH_FOREACH_END(); + if (ce->num_hooked_props) { zend_property_info *prop; ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) {