diff options
Diffstat (limited to 'Zend/zend_inheritance.c')
-rw-r--r-- | Zend/zend_inheritance.c | 97 |
1 files changed, 62 insertions, 35 deletions
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 1c10da5616..3725c00ccc 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -29,7 +29,8 @@ static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce); static void add_compatibility_obligation( - zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn); + zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn, + zend_class_entry *parent_scope); static void add_property_compatibility_obligation( zend_class_entry *ce, const zend_property_info *child_prop, const zend_property_info *parent_prop); @@ -496,8 +497,8 @@ static inheritance_status zend_perform_covariant_type_check( /* }}} */ static inheritance_status zend_do_perform_arg_type_hint_check( - const zend_function *fe, zend_arg_info *fe_arg_info, - const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */ + zend_class_entry *fe_scope, zend_arg_info *fe_arg_info, + zend_class_entry *proto_scope, zend_arg_info *proto_arg_info) /* {{{ */ { if (!ZEND_TYPE_IS_SET(fe_arg_info->type)) { /* Child with no type is always compatible */ @@ -512,12 +513,16 @@ static inheritance_status zend_do_perform_arg_type_hint_check( /* Contravariant type check is performed as a covariant type check with swapped * argument order. */ return zend_perform_covariant_type_check( - proto->common.scope, proto_arg_info->type, fe->common.scope, fe_arg_info->type); + proto_scope, proto_arg_info->type, fe_scope, fe_arg_info->type); } /* }}} */ +/* For abstract trait methods, proto_scope will differ from proto->common.scope, + * as self will refer to the self of the class the trait is used in, not the trait + * the method was declared in. */ static inheritance_status zend_do_perform_implementation_check( - const zend_function *fe, const zend_function *proto) /* {{{ */ + const zend_function *fe, const zend_function *proto, + zend_class_entry *proto_scope) /* {{{ */ { uint32_t i, num_args, proto_num_args, fe_num_args; inheritance_status status, local_status; @@ -578,7 +583,8 @@ static inheritance_status zend_do_perform_implementation_check( return INHERITANCE_ERROR; } - local_status = zend_do_perform_arg_type_hint_check(fe, fe_arg_info, proto, proto_arg_info); + local_status = zend_do_perform_arg_type_hint_check( + fe->common.scope, fe_arg_info, proto_scope, proto_arg_info); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { if (UNEXPECTED(local_status == INHERITANCE_ERROR)) { @@ -604,7 +610,7 @@ static inheritance_status zend_do_perform_implementation_check( local_status = zend_perform_covariant_type_check( fe->common.scope, fe->common.arg_info[-1].type, - proto->common.scope, proto->common.arg_info[-1].type); + proto_scope, proto->common.arg_info[-1].type); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { if (UNEXPECTED(local_status == INHERITANCE_ERROR)) { @@ -619,10 +625,11 @@ static inheritance_status zend_do_perform_implementation_check( } /* }}} */ -static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */ +static ZEND_COLD void zend_append_type_hint( + smart_str *str, zend_class_entry *scope, zend_arg_info *arg_info, int return_hint) /* {{{ */ { if (ZEND_TYPE_IS_SET(arg_info->type)) { - zend_string *type_str = zend_type_to_string_resolved(arg_info->type, fptr->common.scope); + zend_string *type_str = zend_type_to_string_resolved(arg_info->type, scope); smart_str_append(str, type_str); zend_string_release(type_str); if (!return_hint) { @@ -632,7 +639,8 @@ static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function } /* }}} */ -static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function *fptr) /* {{{ */ +static ZEND_COLD zend_string *zend_get_function_declaration( + const zend_function *fptr, zend_class_entry *scope) /* {{{ */ { smart_str str = {0}; @@ -659,7 +667,7 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function num_args++; } for (i = 0; i < num_args;) { - zend_append_type_hint(&str, fptr, arg_info, 0); + zend_append_type_hint(&str, scope, arg_info, 0); if (ZEND_ARG_SEND_MODE(arg_info)) { smart_str_appendc(&str, '&'); @@ -752,7 +760,7 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { smart_str_appends(&str, ": "); - zend_append_type_hint(&str, fptr, fptr->common.arg_info - 1, 1); + zend_append_type_hint(&str, scope, fptr->common.arg_info - 1, 1); } smart_str_0(&str); @@ -765,10 +773,10 @@ static zend_always_inline uint32_t func_lineno(const zend_function *fn) { } static void ZEND_COLD emit_incompatible_method_error( - const zend_function *child, const zend_function *parent, + const zend_function *child, const zend_function *parent, zend_class_entry *parent_scope, inheritance_status status) { - zend_string *parent_prototype = zend_get_function_declaration(parent); - zend_string *child_prototype = zend_get_function_declaration(child); + zend_string *parent_prototype = zend_get_function_declaration(parent, parent_scope); + zend_string *child_prototype = zend_get_function_declaration(child, child->common.scope); if (status == INHERITANCE_UNRESOLVED) { /* Fetch the first unresolved class from registered autoloads */ zend_string *unresolved_class = NULL; @@ -791,20 +799,23 @@ static void ZEND_COLD emit_incompatible_method_error( static void perform_delayable_implementation_check( zend_class_entry *ce, const zend_function *fe, - const zend_function *proto) + const zend_function *proto, zend_class_entry *proto_scope) { - inheritance_status status = zend_do_perform_implementation_check(fe, proto); + inheritance_status status = zend_do_perform_implementation_check(fe, proto, proto_scope); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { - add_compatibility_obligation(ce, fe, proto); + add_compatibility_obligation(ce, fe, proto, proto_scope); } else { ZEND_ASSERT(status == INHERITANCE_ERROR); - emit_incompatible_method_error(fe, proto, status); + emit_incompatible_method_error(fe, proto, proto_scope, status); } } } -static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv, zend_bool check_visibility, zend_bool check_only, zend_bool checked) /* {{{ */ +static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( + zend_function *child, zend_function *parent, + zend_class_entry *ce, zend_class_entry *parent_scope, zval *child_zv, + zend_bool check_visibility, zend_bool check_only, zend_bool checked) /* {{{ */ { uint32_t child_flags; uint32_t parent_flags = parent->common.fn_flags; @@ -899,17 +910,20 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z if (!checked) { if (check_only) { - return zend_do_perform_implementation_check(child, parent); + return zend_do_perform_implementation_check(child, parent, parent_scope); } - perform_delayable_implementation_check(ce, child, parent); + perform_delayable_implementation_check(ce, child, parent, parent_scope); } return INHERITANCE_SUCCESS; } /* }}} */ -static zend_never_inline void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv, zend_bool check_visibility) /* {{{ */ +static zend_never_inline void do_inheritance_check_on_method( + zend_function *child, zend_function *parent, + zend_class_entry *ce, zend_class_entry *parent_scope, + zval *child_zv, zend_bool check_visibility) /* {{{ */ { - do_inheritance_check_on_method_ex(child, parent, ce, child_zv, check_visibility, 0, 0); + do_inheritance_check_on_method_ex(child, parent, ce, parent_scope, child_zv, check_visibility, 0, 0); } /* }}} */ @@ -927,9 +941,11 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function if (checked) { do_inheritance_check_on_method_ex( - func, parent, ce, child, /* check_visibility */ 1, 0, checked); + func, parent, ce, parent->common.scope, child, + /* check_visibility */ 1, 0, checked); } else { - do_inheritance_check_on_method(func, parent, ce, child, /* check_visibility */ 1); + do_inheritance_check_on_method( + func, parent, ce, parent->common.scope, child, /* check_visibility */ 1); } } else { @@ -1589,8 +1605,13 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ /* "abstract private" methods in traits were not available prior to PHP 8. * As such, "abstract protected" was sometimes used to indicate trait requirements, * even though the "implementing" method was private. Do not check visibility - * requirements to maintain backwards-compatibility with such usage. */ - do_inheritance_check_on_method(existing_fn, fn, ce, NULL, /* check_visibility */ 0); + * requirements to maintain backwards-compatibility with such usage. + * + * The parent_scope passed here is not fn->common.scope, because we want "self" to be + * resolved against the using class, not the declaring trait. + */ + do_inheritance_check_on_method( + existing_fn, fn, ce, /* parent_scope */ ce, NULL, /* check_visibility */ 0); return; } @@ -1607,7 +1628,8 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ } else { /* inherited members are overridden by members inserted by traits */ /* check whether the trait method fulfills the inheritance requirements */ - do_inheritance_check_on_method(fn, existing_fn, ce, NULL, /* check_visibility */ 1); + do_inheritance_check_on_method( + fn, existing_fn, ce, existing_fn->common.scope, NULL, /* check_visibility */ 1); } } @@ -2182,6 +2204,7 @@ typedef struct { * so use copies of functions here as well. */ zend_function parent_fn; zend_function child_fn; + zend_class_entry *parent_scope; }; struct { const zend_property_info *parent_prop; @@ -2229,7 +2252,8 @@ static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *de } static void add_compatibility_obligation( - zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn) { + zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn, + zend_class_entry *parent_scope) { HashTable *obligations = get_or_init_obligations_for_class(ce); variance_obligation *obligation = emalloc(sizeof(variance_obligation)); obligation->type = OBLIGATION_COMPATIBILITY; @@ -2244,6 +2268,7 @@ static void add_compatibility_obligation( } else { memcpy(&obligation->parent_fn, parent_fn, sizeof(zend_op_array)); } + obligation->parent_scope = parent_scope; zend_hash_next_index_insert_ptr(obligations, obligation); } @@ -2272,13 +2297,14 @@ static int check_variance_obligation(zval *zv) { } } else if (obligation->type == OBLIGATION_COMPATIBILITY) { inheritance_status status = zend_do_perform_implementation_check( - &obligation->child_fn, &obligation->parent_fn); + &obligation->child_fn, &obligation->parent_fn, obligation->parent_scope); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { return ZEND_HASH_APPLY_KEEP; } ZEND_ASSERT(status == INHERITANCE_ERROR); - emit_incompatible_method_error(&obligation->child_fn, &obligation->parent_fn, status); + emit_incompatible_method_error( + &obligation->child_fn, &obligation->parent_fn, obligation->parent_scope, status); } /* Either the compatibility check was successful or only threw a warning. */ } else { @@ -2345,10 +2371,10 @@ static void report_variance_errors(zend_class_entry *ce) { /* Just used to populate the delayed_autoloads table, * which will be used when printing the "unresolved" error. */ inheritance_status status = zend_do_perform_implementation_check( - &obligation->child_fn, &obligation->parent_fn); + &obligation->child_fn, &obligation->parent_fn, obligation->parent_scope); ZEND_ASSERT(status == INHERITANCE_UNRESOLVED); emit_incompatible_method_error( - &obligation->child_fn, &obligation->parent_fn, status); + &obligation->child_fn, &obligation->parent_fn, obligation->parent_scope, status); } else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) { emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop); } else { @@ -2475,7 +2501,8 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e zend_function *child_func = Z_FUNC_P(zv); inheritance_status status = do_inheritance_check_on_method_ex( - child_func, parent_func, ce, NULL, /* check_visibility */ 1, 1, 0); + child_func, parent_func, ce, parent_func->common.scope, NULL, + /* check_visibility */ 1, 1, 0); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { return status; |