From 49ef6e209d8fbcb4694ecd59b9078498f0dffb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 29 Jun 2023 20:23:53 +0200 Subject: [PATCH] RFC: Add #[Override] attribute (#9836) * Add #[Override] attribute * Move #[\Override] tests into Zend/tests/attributes/override/ * Check `check_only` before removing `ZEND_ACC_OVERRIDE` * NEWS/UPGRADING for #[\Override] --- NEWS | 1 + UPGRADING | 3 ++ Zend/tests/attributes/override/001.phpt | 47 +++++++++++++++++++++++++ Zend/tests/attributes/override/002.phpt | 19 ++++++++++ Zend/tests/attributes/override/003.phpt | 16 +++++++++ Zend/tests/attributes/override/004.phpt | 21 +++++++++++ Zend/tests/attributes/override/005.phpt | 21 +++++++++++ Zend/tests/attributes/override/006.phpt | 26 ++++++++++++++ Zend/tests/attributes/override/007.phpt | 15 ++++++++ Zend/tests/attributes/override/008.phpt | 19 ++++++++++ Zend/tests/attributes/override/009.phpt | 23 ++++++++++++ Zend/tests/attributes/override/010.phpt | 19 ++++++++++ Zend/tests/attributes/override/011.phpt | 19 ++++++++++ Zend/tests/attributes/override/012.phpt | 19 ++++++++++ Zend/tests/attributes/override/013.phpt | 19 ++++++++++ Zend/tests/attributes/override/014.phpt | 18 ++++++++++ Zend/tests/attributes/override/015.phpt | 22 ++++++++++++ Zend/tests/attributes/override/016.phpt | 21 +++++++++++ Zend/tests/attributes/override/017.phpt | 21 +++++++++++ Zend/tests/attributes/override/018.phpt | 32 +++++++++++++++++ Zend/tests/attributes/override/019.phpt | 19 ++++++++++ Zend/tests/attributes/override/020.phpt | 21 +++++++++++ Zend/tests/attributes/override/021.phpt | 19 ++++++++++ Zend/tests/attributes/override/022.phpt | 16 +++++++++ Zend/tests/attributes/override/023.phpt | 19 ++++++++++ Zend/zend_attributes.c | 9 +++++ Zend/zend_attributes.h | 1 + Zend/zend_attributes.stub.php | 9 +++++ Zend/zend_attributes_arginfo.h | 29 ++++++++++++++- Zend/zend_compile.c | 12 +++++++ Zend/zend_compile.h | 5 ++- Zend/zend_inheritance.c | 31 ++++++++++++++++ Zend/zend_inheritance.h | 2 ++ 33 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/attributes/override/001.phpt create mode 100644 Zend/tests/attributes/override/002.phpt create mode 100644 Zend/tests/attributes/override/003.phpt create mode 100644 Zend/tests/attributes/override/004.phpt create mode 100644 Zend/tests/attributes/override/005.phpt create mode 100644 Zend/tests/attributes/override/006.phpt create mode 100644 Zend/tests/attributes/override/007.phpt create mode 100644 Zend/tests/attributes/override/008.phpt create mode 100644 Zend/tests/attributes/override/009.phpt create mode 100644 Zend/tests/attributes/override/010.phpt create mode 100644 Zend/tests/attributes/override/011.phpt create mode 100644 Zend/tests/attributes/override/012.phpt create mode 100644 Zend/tests/attributes/override/013.phpt create mode 100644 Zend/tests/attributes/override/014.phpt create mode 100644 Zend/tests/attributes/override/015.phpt create mode 100644 Zend/tests/attributes/override/016.phpt create mode 100644 Zend/tests/attributes/override/017.phpt create mode 100644 Zend/tests/attributes/override/018.phpt create mode 100644 Zend/tests/attributes/override/019.phpt create mode 100644 Zend/tests/attributes/override/020.phpt create mode 100644 Zend/tests/attributes/override/021.phpt create mode 100644 Zend/tests/attributes/override/022.phpt create mode 100644 Zend/tests/attributes/override/023.phpt diff --git a/NEWS b/NEWS index 9d3e4184123..800f9796ae5 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ PHP NEWS (ilutov) . Fixed GH-11488 (Missing "Optional parameter before required" deprecation on union null type). (ilutov) + . Implement the #[\Override] attribute RFC. (timwolla) - DOM: . Fixed bug GH-11500 (Namespace reuse in createElementNS() generates wrong diff --git a/UPGRADING b/UPGRADING index 2f3e868f191..37c99586ac4 100644 --- a/UPGRADING +++ b/UPGRADING @@ -83,6 +83,9 @@ PHP 8.3 UPGRADE NOTES declarations. RFC: https://wiki.php.net/rfc/typed_class_constants . Closures created from magic methods can now accept named arguments. . The final modifier may now be used when using a method from a trait. + . Added the #[\Override] attribute to check that a method exists + in a parent class or implemented interface. + RFC: https://wiki.php.net/rfc/marking_overriden_methods - Posix . posix_getrlimit() now takes an optional $res parameter to allow fetching a diff --git a/Zend/tests/attributes/override/001.phpt b/Zend/tests/attributes/override/001.phpt new file mode 100644 index 00000000000..7eefcfe3a58 --- /dev/null +++ b/Zend/tests/attributes/override/001.phpt @@ -0,0 +1,47 @@ +--TEST-- +#[\Override] +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/002.phpt b/Zend/tests/attributes/override/002.phpt new file mode 100644 index 00000000000..41188ecaf5e --- /dev/null +++ b/Zend/tests/attributes/override/002.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Valid native interface. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/003.phpt b/Zend/tests/attributes/override/003.phpt new file mode 100644 index 00000000000..9e91c739442 --- /dev/null +++ b/Zend/tests/attributes/override/003.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\Override]: No parent class. +--FILE-- + +--EXPECTF-- +Fatal error: C::c() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/004.phpt b/Zend/tests/attributes/override/004.phpt new file mode 100644 index 00000000000..197a9a9e723 --- /dev/null +++ b/Zend/tests/attributes/override/004.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: No parent class, but child implements matching interface. +--FILE-- + +--EXPECTF-- +Fatal error: P::i() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/005.phpt b/Zend/tests/attributes/override/005.phpt new file mode 100644 index 00000000000..8e346b76b88 --- /dev/null +++ b/Zend/tests/attributes/override/005.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: No parent class, but child implements matching interface (2). +--FILE-- + +--EXPECTF-- +Fatal error: P::i() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/006.phpt b/Zend/tests/attributes/override/006.phpt new file mode 100644 index 00000000000..59066901550 --- /dev/null +++ b/Zend/tests/attributes/override/006.phpt @@ -0,0 +1,26 @@ +--TEST-- +#[\Override]: No parent interface. +--FILE-- + +--EXPECTF-- +Fatal error: I::i() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/007.phpt b/Zend/tests/attributes/override/007.phpt new file mode 100644 index 00000000000..561d4dcf789 --- /dev/null +++ b/Zend/tests/attributes/override/007.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: On trait. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/008.phpt b/Zend/tests/attributes/override/008.phpt new file mode 100644 index 00000000000..095d95f9bd2 --- /dev/null +++ b/Zend/tests/attributes/override/008.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: On used trait without parent method. +--FILE-- + +--EXPECTF-- +Fatal error: Foo::t() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/009.phpt b/Zend/tests/attributes/override/009.phpt new file mode 100644 index 00000000000..bd8b328cb17 --- /dev/null +++ b/Zend/tests/attributes/override/009.phpt @@ -0,0 +1,23 @@ +--TEST-- +#[\Override]: On used trait with interface method. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/010.phpt b/Zend/tests/attributes/override/010.phpt new file mode 100644 index 00000000000..758c78b3213 --- /dev/null +++ b/Zend/tests/attributes/override/010.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Parent method is private. +--FILE-- + +--EXPECTF-- +Fatal error: C::p() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/011.phpt b/Zend/tests/attributes/override/011.phpt new file mode 100644 index 00000000000..06fb047c33a --- /dev/null +++ b/Zend/tests/attributes/override/011.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Parent method is private (2). +--FILE-- + +--EXPECTF-- +Fatal error: C::p() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/012.phpt b/Zend/tests/attributes/override/012.phpt new file mode 100644 index 00000000000..cf649879812 --- /dev/null +++ b/Zend/tests/attributes/override/012.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Parent method is protected. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/013.phpt b/Zend/tests/attributes/override/013.phpt new file mode 100644 index 00000000000..2db22af3425 --- /dev/null +++ b/Zend/tests/attributes/override/013.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Parent method is protected (2). +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/014.phpt b/Zend/tests/attributes/override/014.phpt new file mode 100644 index 00000000000..6451e25cb06 --- /dev/null +++ b/Zend/tests/attributes/override/014.phpt @@ -0,0 +1,18 @@ +--TEST-- +#[\Override]: enum without interface. +--FILE-- + +--EXPECTF-- +Fatal error: E::e() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/015.phpt b/Zend/tests/attributes/override/015.phpt new file mode 100644 index 00000000000..7e4e19c61a5 --- /dev/null +++ b/Zend/tests/attributes/override/015.phpt @@ -0,0 +1,22 @@ +--TEST-- +#[\Override]: enum with matching interface. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/016.phpt b/Zend/tests/attributes/override/016.phpt new file mode 100644 index 00000000000..2c1d61a17b2 --- /dev/null +++ b/Zend/tests/attributes/override/016.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Declared abstract trait method. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/017.phpt b/Zend/tests/attributes/override/017.phpt new file mode 100644 index 00000000000..c89589463c0 --- /dev/null +++ b/Zend/tests/attributes/override/017.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Redeclared trait method. +--FILE-- + +--EXPECTF-- +Fatal error: C::t() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/018.phpt b/Zend/tests/attributes/override/018.phpt new file mode 100644 index 00000000000..0d008dcabf8 --- /dev/null +++ b/Zend/tests/attributes/override/018.phpt @@ -0,0 +1,32 @@ +--TEST-- +#[\Override]: Redeclared trait method with interface. +--FILE-- +i()); + +echo "Done"; + +?> +--EXPECT-- +string(1) "C" +Done diff --git a/Zend/tests/attributes/override/019.phpt b/Zend/tests/attributes/override/019.phpt new file mode 100644 index 00000000000..f49bc8fd69a --- /dev/null +++ b/Zend/tests/attributes/override/019.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Valid anonymous class. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/020.phpt b/Zend/tests/attributes/override/020.phpt new file mode 100644 index 00000000000..0b6161b8feb --- /dev/null +++ b/Zend/tests/attributes/override/020.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Invalid anonymous class. +--FILE-- + +--EXPECTF-- +Fatal error: I@anonymous::c() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/021.phpt b/Zend/tests/attributes/override/021.phpt new file mode 100644 index 00000000000..678038d99f5 --- /dev/null +++ b/Zend/tests/attributes/override/021.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: __construct +--FILE-- + +--EXPECTF-- +Fatal error: C::__construct() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/022.phpt b/Zend/tests/attributes/override/022.phpt new file mode 100644 index 00000000000..9e98fbf7cf0 --- /dev/null +++ b/Zend/tests/attributes/override/022.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\Override]: Static method no parent class. +--FILE-- + +--EXPECTF-- +Fatal error: C::c() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/attributes/override/023.phpt b/Zend/tests/attributes/override/023.phpt new file mode 100644 index 00000000000..2cdc05576ed --- /dev/null +++ b/Zend/tests/attributes/override/023.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Static method. +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 83c3c928c56..798dd27b7b6 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -29,6 +29,7 @@ ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute; ZEND_API zend_class_entry *zend_ce_allow_dynamic_properties; ZEND_API zend_class_entry *zend_ce_sensitive_parameter; ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value; +ZEND_API zend_class_entry *zend_ce_override; static zend_object_handlers attributes_object_handlers_sensitive_parameter_value; @@ -136,6 +137,11 @@ static HashTable *attributes_sensitive_parameter_value_get_properties_for(zend_o return NULL; } +ZEND_METHOD(Override, __construct) +{ + ZEND_PARSE_PARAMETERS_NONE(); +} + static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) { if (attributes) { @@ -371,6 +377,9 @@ void zend_register_attribute_ce(void) /* This is not an actual attribute, thus the zend_mark_internal_attribute() call is missing. */ zend_ce_sensitive_parameter_value = register_class_SensitiveParameterValue(); zend_ce_sensitive_parameter_value->default_object_handlers = &attributes_object_handlers_sensitive_parameter_value; + + zend_ce_override = register_class_Override(); + zend_mark_internal_attribute(zend_ce_override); } void zend_attributes_shutdown(void) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index fc02a7d656e..f8635aae1d3 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -43,6 +43,7 @@ extern ZEND_API zend_class_entry *zend_ce_attribute; extern ZEND_API zend_class_entry *zend_ce_allow_dynamic_properties; extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter; extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value; +extern ZEND_API zend_class_entry *zend_ce_override; typedef struct { zend_string *name; diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index df13c9f8246..c3ed7b67460 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -86,3 +86,12 @@ final class SensitiveParameterValue public function __debugInfo(): array {} } + +/** + * @strict-properties + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class Override +{ + public function __construct() {} +} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 3bcdcd70741..fdc96509447 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: afb6a3f1d14099066d028b1579fff074359da293 */ + * Stub hash: ab85fd8b8d2b1f1d2bc6c72c9663b112b8d6d2f8 */ 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") @@ -22,6 +22,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SensitiveParameterValue___debugInfo, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() +#define arginfo_class_Override___construct arginfo_class_ReturnTypeWillChange___construct + ZEND_METHOD(Attribute, __construct); ZEND_METHOD(ReturnTypeWillChange, __construct); @@ -30,6 +32,7 @@ ZEND_METHOD(SensitiveParameter, __construct); ZEND_METHOD(SensitiveParameterValue, __construct); ZEND_METHOD(SensitiveParameterValue, getValue); ZEND_METHOD(SensitiveParameterValue, __debugInfo); +ZEND_METHOD(Override, __construct); static const zend_function_entry class_Attribute_methods[] = { @@ -63,6 +66,12 @@ static const zend_function_entry class_SensitiveParameterValue_methods[] = { ZEND_FE_END }; + +static const zend_function_entry class_Override_methods[] = { + ZEND_ME(Override, __construct, arginfo_class_Override___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_Attribute(void) { zend_class_entry ce, *class_entry; @@ -205,3 +214,21 @@ static zend_class_entry *register_class_SensitiveParameterValue(void) return class_entry; } + +static zend_class_entry *register_class_Override(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Override", class_Override_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + + zend_string *attribute_name_Attribute_class_Override = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_Override = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Override, 1); + zend_string_release(attribute_name_Attribute_class_Override); + zval attribute_Attribute_class_Override_arg0; + ZVAL_LONG(&attribute_Attribute_class_Override_arg0, ZEND_ATTRIBUTE_TARGET_METHOD); + ZVAL_COPY_VALUE(&attribute_Attribute_class_Override->args[0].value, &attribute_Attribute_class_Override_arg0); + + return class_entry; +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 04827557c7e..592e4ca4560 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7538,6 +7538,16 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) } zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0); + + zend_attribute *override_attribute = zend_get_attribute_str( + op_array->attributes, + "override", + sizeof("override")-1 + ); + + if (override_attribute) { + op_array->fn_flags |= ZEND_ACC_OVERRIDE; + } } /* Do not leak the class scope into free standing functions, even if they are dynamically @@ -8106,6 +8116,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) } else if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) { zend_string_release(lcname); zend_build_properties_info_table(ce); + zend_inheritance_check_override(ce); ce->ce_flags |= ZEND_ACC_LINKED; zend_observer_class_linked_notify(ce, lcname); return; @@ -8116,6 +8127,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) link_unbound: /* Link unbound simple class */ zend_build_properties_info_table(ce); + zend_inheritance_check_override(ce); ce->ce_flags |= ZEND_ACC_LINKED; } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d474d13e86d..724b5b8c8a5 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -304,7 +304,7 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ -/* Function Flags (unused: 28-30) | | | */ +/* Function Flags (unused: 29-30) | | | */ /* ============== | | | */ /* | | | */ /* deprecation flag | | | */ @@ -363,6 +363,9 @@ 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 | | */ +/* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index fb3623fdc41..ce7b1f67ea5 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1174,6 +1174,11 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( } perform_delayable_implementation_check(ce, child, child_scope, parent, parent_scope); } + + if (!check_only && child->common.scope == ce) { + child->common.fn_flags &= ~ZEND_ACC_OVERRIDE; + } + return INHERITANCE_SUCCESS; } /* }}} */ @@ -1896,6 +1901,28 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry } /* }}} */ + +void zend_inheritance_check_override(zend_class_entry *ce) +{ + zend_function *f; + + if (ce->ce_flags & ZEND_ACC_TRAIT) { + return; + } + + ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, f) { + if (f->common.fn_flags & ZEND_ACC_OVERRIDE) { + ZEND_ASSERT(f->type != ZEND_INTERNAL_FUNCTION); + + zend_error_at_noreturn( + E_COMPILE_ERROR, f->op_array.filename, f->op_array.line_start, + "%s::%s() has #[\\Override] attribute, but no matching parent method exists", + ZEND_FN_SCOPE_NAME(f), ZSTR_VAL(f->common.function_name)); + } + } ZEND_HASH_FOREACH_END(); +} + + static zend_class_entry *fixup_trait_scope(const zend_function *fn, zend_class_entry *ce) { /* self in trait methods should be resolved to the using class, not the trait. */ @@ -2772,6 +2799,8 @@ static void resolve_delayed_variance_obligations(zend_class_entry *ce) { check_variance_obligation(obligation); } ZEND_HASH_FOREACH_END(); + zend_inheritance_check_override(ce); + ce->ce_flags &= ~ZEND_ACC_UNRESOLVED_VARIANCE; ce->ce_flags |= ZEND_ACC_LINKED; zend_hash_index_del(all_obligations, num_key); @@ -3131,6 +3160,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string EG(record_errors) = orig_record_errors; if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) { + zend_inheritance_check_override(ce); ce->ce_flags |= ZEND_ACC_LINKED; } else { ce->ce_flags |= ZEND_ACC_NEARLY_LINKED; @@ -3342,6 +3372,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { zend_verify_abstract_class(ce); } + zend_inheritance_check_override(ce); ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)); ce->ce_flags |= ZEND_ACC_LINKED; diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index 1ecd6017f15..96ead3bf3e5 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -37,6 +37,8 @@ void zend_verify_abstract_class(zend_class_entry *ce); void zend_build_properties_info_table(zend_class_entry *ce); ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding); +void zend_inheritance_check_override(zend_class_entry *ce); + ZEND_API extern zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces); ZEND_API extern zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies);