diff options
Diffstat (limited to 'Zend/zend_inheritance.c')
-rw-r--r-- | Zend/zend_inheritance.c | 494 |
1 files changed, 233 insertions, 261 deletions
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 0dbb5b77a6..220df561a2 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -29,8 +29,7 @@ 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_bool always_error); + zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn); static void add_property_compatibility_obligation( zend_class_entry *ce, const zend_property_info *child_prop, const zend_property_info *parent_prop); @@ -41,14 +40,31 @@ static void overridden_ptr_dtor(zval *zv) /* {{{ */ } /* }}} */ +static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) { + if (ZEND_TYPE_HAS_LIST(*type)) { + zend_type_list *old_list = ZEND_TYPE_LIST(*type); + size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); + zend_type_list *new_list = ZEND_TYPE_USES_ARENA(*type) + ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); + memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); + ZEND_TYPE_SET_PTR(*type, new_list); + + void *entry; + ZEND_TYPE_LIST_FOREACH(new_list, entry) { + ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry)); + zend_string_addref(ZEND_TYPE_LIST_GET_NAME(entry)); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_NAME(*type)) { + zend_string_addref(ZEND_TYPE_NAME(*type)); + } +} + static zend_property_info *zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */ { zend_property_info* new_property_info = pemalloc(sizeof(zend_property_info), 1); memcpy(new_property_info, property_info, sizeof(zend_property_info)); zend_string_addref(new_property_info->name); - if (ZEND_TYPE_IS_NAME(new_property_info->type)) { - zend_string_addref(ZEND_TYPE_NAME(new_property_info->type)); - } + zend_type_copy_ctor(&new_property_info->type, /* persistent */ 1); return new_property_info; } @@ -220,7 +236,8 @@ static zend_bool class_visible(zend_class_entry *ce) { } } -static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name) { +static zend_class_entry *lookup_class( + zend_class_entry *scope, zend_string *name, zend_bool register_unresolved) { zend_class_entry *ce; if (!CG(in_compilation)) { uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD; @@ -229,12 +246,14 @@ static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name return ce; } - /* We'll autoload this class and process delayed variance obligations later. */ - if (!CG(delayed_autoloads)) { - ALLOC_HASHTABLE(CG(delayed_autoloads)); - zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0); + if (register_unresolved) { + /* We'll autoload this class and process delayed variance obligations later. */ + if (!CG(delayed_autoloads)) { + ALLOC_HASHTABLE(CG(delayed_autoloads)); + zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0); + } + zend_hash_add_empty_element(CG(delayed_autoloads), name); } - zend_hash_add_empty_element(CG(delayed_autoloads), name); } else { ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (ce && class_visible(ce)) { @@ -303,6 +322,23 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce return 0; } +static zend_bool zend_type_contains_traversable(zend_type type) { + if (ZEND_TYPE_HAS_LIST(type)) { + void *entry; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), entry) { + ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry)); + if (zend_string_equals_literal_ci(ZEND_TYPE_LIST_GET_NAME(entry), "Traversable")) { + return 1; + } + } ZEND_TYPE_LIST_FOREACH_END(); + return 0; + } + if (ZEND_TYPE_HAS_NAME(type)) { + return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "Traversable"); + } + return 0; +} + /* Unresolved means that class declarations that are currently not available are needed to * determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated * as an ERROR. */ @@ -312,85 +348,150 @@ typedef enum { INHERITANCE_SUCCESS = 1, } inheritance_status; -static inheritance_status zend_perform_covariant_type_check( - zend_string **unresolved_class, - const zend_function *fe, zend_arg_info *fe_arg_info, - const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */ -{ - zend_type fe_type = fe_arg_info->type, proto_type = proto_arg_info->type; - ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type)); - - if (ZEND_TYPE_ALLOW_NULL(fe_type) && !ZEND_TYPE_ALLOW_NULL(proto_type)) { - return INHERITANCE_ERROR; +static inheritance_status zend_perform_covariant_class_type_check( + zend_class_entry *fe_scope, zend_string *fe_class_name, + zend_class_entry *proto_scope, zend_type proto_type, + zend_bool register_unresolved) { + zend_bool have_unresolved = 0; + zend_class_entry *fe_ce = NULL; + if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_OBJECT) { + /* Currently, any class name would be allowed here. We still perform a class lookup + * for forward-compatibility reasons, as we may have named types in the future that + * are not classes (such as enums or typedefs). */ + if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved); + if (!fe_ce) { + have_unresolved = 1; + } else { + return INHERITANCE_SUCCESS; + } } - - if (ZEND_TYPE_IS_CLASS(proto_type)) { - zend_string *fe_class_name, *proto_class_name; - zend_class_entry *fe_ce, *proto_ce; - if (!ZEND_TYPE_IS_CLASS(fe_type)) { - return INHERITANCE_ERROR; + if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_ITERABLE) { + if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved); + if (!fe_ce) { + have_unresolved = 1; + } else if (unlinked_instanceof(fe_ce, zend_ce_traversable)) { + return INHERITANCE_SUCCESS; } - - fe_class_name = resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type)); - proto_class_name = resolve_class_name(proto->common.scope, ZEND_TYPE_NAME(proto_type)); + } + if (ZEND_TYPE_HAS_NAME(proto_type)) { + zend_string *proto_class_name = resolve_class_name(proto_scope, ZEND_TYPE_NAME(proto_type)); if (zend_string_equals_ci(fe_class_name, proto_class_name)) { return INHERITANCE_SUCCESS; } /* Make sure to always load both classes, to avoid only registering one of them as * a delayed autoload. */ - fe_ce = lookup_class(fe->common.scope, fe_class_name); - proto_ce = lookup_class(proto->common.scope, proto_class_name); - if (!fe_ce) { - *unresolved_class = fe_class_name; - return INHERITANCE_UNRESOLVED; - } - if (!proto_ce) { - *unresolved_class = proto_class_name; - return INHERITANCE_UNRESOLVED; + if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved); + zend_class_entry *proto_ce = + lookup_class(proto_scope, proto_class_name, register_unresolved); + if (!fe_ce || !proto_ce) { + have_unresolved = 1; + } else if (unlinked_instanceof(fe_ce, proto_ce)) { + return INHERITANCE_SUCCESS; } + } + if (ZEND_TYPE_HAS_LIST(proto_type)) { + void *entry; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(proto_type), entry) { + ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry)); + zend_string *proto_class_name = + resolve_class_name(proto_scope, ZEND_TYPE_LIST_GET_NAME(entry)); + if (zend_string_equals_ci(fe_class_name, proto_class_name)) { + return INHERITANCE_SUCCESS; + } - return unlinked_instanceof(fe_ce, proto_ce) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; - } else if (ZEND_TYPE_CODE(proto_type) == IS_ITERABLE) { - if (ZEND_TYPE_IS_CLASS(fe_type)) { - zend_string *fe_class_name = - resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type)); - zend_class_entry *fe_ce = lookup_class(fe->common.scope, fe_class_name); - if (!fe_ce) { - *unresolved_class = fe_class_name; - return INHERITANCE_UNRESOLVED; + if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved); + zend_class_entry *proto_ce = + lookup_class(proto_scope, proto_class_name, register_unresolved); + if (!fe_ce || !proto_ce) { + have_unresolved = 1; + } else if (unlinked_instanceof(fe_ce, proto_ce)) { + return INHERITANCE_SUCCESS; } - return unlinked_instanceof(fe_ce, zend_ce_traversable) - ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; + } ZEND_TYPE_LIST_FOREACH_END(); + } + return have_unresolved ? INHERITANCE_UNRESOLVED : INHERITANCE_ERROR; +} + +static inheritance_status zend_perform_covariant_type_check( + zend_class_entry *fe_scope, zend_type fe_type, + zend_class_entry *proto_scope, zend_type proto_type) /* {{{ */ +{ + ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type)); + + /* Builtin types may be removed, but not added */ + uint32_t fe_type_mask = ZEND_TYPE_PURE_MASK(fe_type); + uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK(proto_type); + uint32_t added_types = fe_type_mask & ~proto_type_mask; + if (added_types) { + // TODO: Make "iterable" an alias of "array|Traversable" instead, + // so these special cases will be handled automatically. + if (added_types == MAY_BE_ITERABLE + && (proto_type_mask & MAY_BE_ARRAY) + && zend_type_contains_traversable(proto_type)) { + /* Replacing array|Traversable with iterable is okay */ + } else if (added_types == MAY_BE_ARRAY && (proto_type_mask & MAY_BE_ITERABLE)) { + /* Replacing iterable with array is okay */ + } else { + /* Otherwise adding new types is illegal */ + return INHERITANCE_ERROR; + } + } + + if (ZEND_TYPE_HAS_NAME(fe_type)) { + zend_string *fe_class_name = resolve_class_name(fe_scope, ZEND_TYPE_NAME(fe_type)); + inheritance_status status = zend_perform_covariant_class_type_check( + fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 0); + if (status != INHERITANCE_UNRESOLVED) { + return status; } - return ZEND_TYPE_CODE(fe_type) == IS_ITERABLE || ZEND_TYPE_CODE(fe_type) == IS_ARRAY - ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; - } else if (ZEND_TYPE_CODE(proto_type) == IS_OBJECT) { - if (ZEND_TYPE_IS_CLASS(fe_type)) { - /* Currently, any class name would be allowed here. We still perform a class lookup - * for forward-compatibility reasons, as we may have named types in the future that - * are not classes (such as enums or typedefs). */ + zend_perform_covariant_class_type_check( + fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 1); + return INHERITANCE_UNRESOLVED; + } + + if (ZEND_TYPE_HAS_LIST(fe_type)) { + void *entry; + zend_bool all_success = 1; + + /* First try to check whether we can succeed without resolving anything */ + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), entry) { + ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry)); zend_string *fe_class_name = - resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type)); - zend_class_entry *fe_ce = lookup_class(fe->common.scope, fe_class_name); - if (!fe_ce) { - *unresolved_class = fe_class_name; - return INHERITANCE_UNRESOLVED; + resolve_class_name(fe_scope, ZEND_TYPE_LIST_GET_NAME(entry)); + inheritance_status status = zend_perform_covariant_class_type_check( + fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 0); + if (status == INHERITANCE_ERROR) { + return INHERITANCE_ERROR; } + + if (status != INHERITANCE_SUCCESS) { + all_success = 0; + } + } ZEND_TYPE_LIST_FOREACH_END(); + + /* All individual checks suceeded, overall success */ + if (all_success) { return INHERITANCE_SUCCESS; } - return ZEND_TYPE_CODE(fe_type) == IS_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; - } else { - return ZEND_TYPE_CODE(fe_type) == ZEND_TYPE_CODE(proto_type) - ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; + /* Register all classes that may have to be resolved */ + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), entry) { + ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry)); + zend_string *fe_class_name = + resolve_class_name(fe_scope, ZEND_TYPE_LIST_GET_NAME(entry)); + zend_perform_covariant_class_type_check( + fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 1); + } ZEND_TYPE_LIST_FOREACH_END(); + return INHERITANCE_UNRESOLVED; } + + return INHERITANCE_SUCCESS; } /* }}} */ static inheritance_status zend_do_perform_arg_type_hint_check( - zend_string **unresolved_class, const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */ { @@ -407,12 +508,12 @@ 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( - unresolved_class, proto, proto_arg_info, fe, fe_arg_info); + proto->common.scope, proto_arg_info->type, fe->common.scope, fe_arg_info->type); } /* }}} */ static inheritance_status zend_do_perform_implementation_check( - zend_string **unresolved_class, const zend_function *fe, const zend_function *proto) /* {{{ */ + const zend_function *fe, const zend_function *proto) /* {{{ */ { uint32_t i, num_args; inheritance_status status, local_status; @@ -478,8 +579,7 @@ static inheritance_status zend_do_perform_implementation_check( proto_arg_info = &proto->common.arg_info[proto->common.num_args]; } - local_status = zend_do_perform_arg_type_hint_check( - unresolved_class, fe, fe_arg_info, proto, proto_arg_info); + local_status = zend_do_perform_arg_type_hint_check(fe, fe_arg_info, proto, proto_arg_info); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { if (UNEXPECTED(local_status == INHERITANCE_ERROR)) { @@ -490,7 +590,7 @@ static inheritance_status zend_do_perform_implementation_check( } /* by-ref constraints on arguments are invariant */ - if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) { + if (ZEND_ARG_SEND_MODE(fe_arg_info) != ZEND_ARG_SEND_MODE(proto_arg_info)) { return INHERITANCE_ERROR; } } @@ -504,7 +604,8 @@ static inheritance_status zend_do_perform_implementation_check( } local_status = zend_perform_covariant_type_check( - unresolved_class, fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1); + fe->common.scope, fe->common.arg_info[-1].type, + proto->common.scope, proto->common.arg_info[-1].type); if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) { if (UNEXPECTED(local_status == INHERITANCE_ERROR)) { @@ -521,33 +622,10 @@ 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) /* {{{ */ { - - if (ZEND_TYPE_IS_SET(arg_info->type) && ZEND_TYPE_ALLOW_NULL(arg_info->type)) { - smart_str_appendc(str, '?'); - } - - if (ZEND_TYPE_IS_CLASS(arg_info->type)) { - const char *class_name; - size_t class_name_len; - - class_name = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)); - class_name_len = ZSTR_LEN(ZEND_TYPE_NAME(arg_info->type)); - - if (!strcasecmp(class_name, "self") && fptr->common.scope) { - class_name = ZSTR_VAL(fptr->common.scope->name); - class_name_len = ZSTR_LEN(fptr->common.scope->name); - } else if (!strcasecmp(class_name, "parent") && fptr->common.scope && fptr->common.scope->parent) { - class_name = ZSTR_VAL(fptr->common.scope->parent->name); - class_name_len = ZSTR_LEN(fptr->common.scope->parent->name); - } - - smart_str_appendl(str, class_name, class_name_len); - if (!return_hint) { - smart_str_appendc(str, ' '); - } - } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - const char *type_name = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)); - smart_str_appends(str, type_name); + if (ZEND_TYPE_IS_SET(arg_info->type)) { + zend_string *type_str = zend_type_to_string_resolved(arg_info->type, fptr->common.scope); + smart_str_append(str, type_str); + zend_string_release(type_str); if (!return_hint) { smart_str_appendc(str, ' '); } @@ -584,11 +662,11 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function for (i = 0; i < num_args;) { zend_append_type_hint(&str, fptr, arg_info, 0); - if (arg_info->pass_by_reference) { + if (ZEND_ARG_SEND_MODE(arg_info)) { smart_str_appendc(&str, '&'); } - if (arg_info->is_variadic) { + if (ZEND_ARG_IS_VARIADIC(arg_info)) { smart_str_appends(&str, "..."); } @@ -605,7 +683,7 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function smart_str_append_unsigned(&str, i); } - if (i >= required && !arg_info->is_variadic) { + if (i >= required && !ZEND_ARG_IS_VARIADIC(arg_info)) { smart_str_appends(&str, " = "); if (fptr->type == ZEND_USER_FUNCTION) { zend_op *precv = NULL; @@ -685,66 +763,41 @@ static zend_always_inline uint32_t func_lineno(const zend_function *fn) { } static void ZEND_COLD emit_incompatible_method_error( - int error_level, const char *error_verb, const zend_function *child, const zend_function *parent, - inheritance_status status, zend_string *unresolved_class) { + inheritance_status status) { zend_string *parent_prototype = zend_get_function_declaration(parent); zend_string *child_prototype = zend_get_function_declaration(child); if (status == INHERITANCE_UNRESOLVED) { - zend_error_at(error_level, NULL, func_lineno(child), + /* Fetch the first unresolved class from registered autoloads */ + zend_string *unresolved_class = NULL; + ZEND_HASH_FOREACH_STR_KEY(CG(delayed_autoloads), unresolved_class) { + break; + } ZEND_HASH_FOREACH_END(); + ZEND_ASSERT(unresolved_class); + + zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child), "Could not check compatibility between %s and %s, because class %s is not available", ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype), ZSTR_VAL(unresolved_class)); } else { - zend_error_at(error_level, NULL, func_lineno(child), - "Declaration of %s %s be compatible with %s", - ZSTR_VAL(child_prototype), error_verb, ZSTR_VAL(parent_prototype)); + zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child), + "Declaration of %s must be compatible with %s", + ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype)); } zend_string_efree(child_prototype); zend_string_efree(parent_prototype); } -static void ZEND_COLD emit_incompatible_method_error_or_warning( - const zend_function *child, const zend_function *parent, - inheritance_status status, zend_string *unresolved_class, zend_bool always_error) { - int error_level; - const char *error_verb; - if (always_error || - (child->common.prototype && - (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) || - ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && - (!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || - zend_perform_covariant_type_check(&unresolved_class, child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) != INHERITANCE_SUCCESS)) - ) { - error_level = E_COMPILE_ERROR; - error_verb = "must"; - } else { - error_level = E_WARNING; - error_verb = "should"; - } - emit_incompatible_method_error( - error_level, error_verb, child, parent, status, unresolved_class); -} - static void perform_delayable_implementation_check( zend_class_entry *ce, const zend_function *fe, - const zend_function *proto, zend_bool always_error) + const zend_function *proto) { - zend_string *unresolved_class; - inheritance_status status = zend_do_perform_implementation_check( - &unresolved_class, fe, proto); - + inheritance_status status = zend_do_perform_implementation_check(fe, proto); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { - add_compatibility_obligation(ce, fe, proto, always_error); + add_compatibility_obligation(ce, fe, proto); } else { ZEND_ASSERT(status == INHERITANCE_ERROR); - if (always_error) { - emit_incompatible_method_error( - E_COMPILE_ERROR, "must", fe, proto, status, unresolved_class); - } else { - emit_incompatible_method_error_or_warning( - fe, proto, status, unresolved_class, always_error); - } + emit_incompatible_method_error(fe, proto, status); } } } @@ -843,13 +896,9 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z if (!checked) { if (check_only) { - zend_string *unresolved_class; - - return zend_do_perform_implementation_check( - &unresolved_class, child, parent); + return zend_do_perform_implementation_check(child, parent); } - perform_delayable_implementation_check( - ce, child, parent, /*always_error*/0); + perform_delayable_implementation_check(ce, child, parent); } return INHERITANCE_SUCCESS; } @@ -897,50 +946,38 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function inheritance_status property_types_compatible( const zend_property_info *parent_info, const zend_property_info *child_info) { - zend_string *parent_name, *child_name; - zend_class_entry *parent_type_ce, *child_type_ce; - if (parent_info->type == child_info->type) { + if (ZEND_TYPE_PURE_MASK(parent_info->type) == ZEND_TYPE_PURE_MASK(child_info->type) + && ZEND_TYPE_NAME(parent_info->type) == ZEND_TYPE_NAME(child_info->type)) { return INHERITANCE_SUCCESS; } - if (!ZEND_TYPE_IS_CLASS(parent_info->type) || !ZEND_TYPE_IS_CLASS(child_info->type) || - ZEND_TYPE_ALLOW_NULL(parent_info->type) != ZEND_TYPE_ALLOW_NULL(child_info->type)) { + if (ZEND_TYPE_IS_SET(parent_info->type) != ZEND_TYPE_IS_SET(child_info->type)) { return INHERITANCE_ERROR; } - parent_name = ZEND_TYPE_IS_CE(parent_info->type) - ? ZEND_TYPE_CE(parent_info->type)->name - : resolve_class_name(parent_info->ce, ZEND_TYPE_NAME(parent_info->type)); - child_name = ZEND_TYPE_IS_CE(child_info->type) - ? ZEND_TYPE_CE(child_info->type)->name - : resolve_class_name(child_info->ce, ZEND_TYPE_NAME(child_info->type)); - if (zend_string_equals_ci(parent_name, child_name)) { + /* Perform a covariant type check in both directions to determined invariance. */ + inheritance_status status1 = zend_perform_covariant_type_check( + child_info->ce, child_info->type, parent_info->ce, parent_info->type); + inheritance_status status2 = zend_perform_covariant_type_check( + parent_info->ce, parent_info->type, child_info->ce, child_info->type); + if (status1 == INHERITANCE_SUCCESS && status2 == INHERITANCE_SUCCESS) { return INHERITANCE_SUCCESS; } - - /* Check for class aliases */ - parent_type_ce = ZEND_TYPE_IS_CE(parent_info->type) - ? ZEND_TYPE_CE(parent_info->type) - : lookup_class(parent_info->ce, parent_name); - child_type_ce = ZEND_TYPE_IS_CE(child_info->type) - ? ZEND_TYPE_CE(child_info->type) - : lookup_class(child_info->ce, child_name); - if (!parent_type_ce || !child_type_ce) { - return INHERITANCE_UNRESOLVED; + if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) { + return INHERITANCE_ERROR; } - return parent_type_ce == child_type_ce ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; + ZEND_ASSERT(status1 == INHERITANCE_UNRESOLVED || status2 == INHERITANCE_UNRESOLVED); + return INHERITANCE_UNRESOLVED; } static void emit_incompatible_property_error( const zend_property_info *child, const zend_property_info *parent) { + zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce); zend_error_noreturn(E_COMPILE_ERROR, - "Type of %s::$%s must be %s%s (as in class %s)", + "Type of %s::$%s must be %s (as in class %s)", ZSTR_VAL(child->ce->name), zend_get_unmangled_property_name(child->name), - ZEND_TYPE_ALLOW_NULL(parent->type) ? "?" : "", - ZEND_TYPE_IS_CLASS(parent->type) - ? ZSTR_VAL(ZEND_TYPE_IS_CE(parent->type) ? ZEND_TYPE_CE(parent->type)->name : resolve_class_name(parent->ce, ZEND_TYPE_NAME(parent->type))) - : zend_get_type_by_const(ZEND_TYPE_CODE(parent->type)), + ZSTR_VAL(type_str), ZSTR_VAL(parent->ce->name)); } @@ -1502,14 +1539,11 @@ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zen ce->serialize_func = fe; } else if (zend_string_equals_literal(mname, "unserialize")) { ce->unserialize_func = fe; - } else if (ZSTR_LEN(ce->name) != ZSTR_LEN(mname) && (ZSTR_VAL(mname)[0] != '_' || ZSTR_VAL(mname)[1] != '_')) { + } else if (ZSTR_VAL(mname)[0] != '_' || ZSTR_VAL(mname)[1] != '_') { /* pass */ } else if (zend_string_equals_literal(mname, ZEND_CLONE_FUNC_NAME)) { ce->clone = fe; } else if (zend_string_equals_literal(mname, ZEND_CONSTRUCTOR_FUNC_NAME)) { - if (ce->constructor && (!ce->parent || ce->constructor != ce->parent->constructor)) { - zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ZSTR_VAL(ce->name)); - } ce->constructor = fe; } else if (zend_string_equals_literal(mname, ZEND_DESTRUCTOR_FUNC_NAME)) { ce->destructor = fe; @@ -1533,17 +1567,6 @@ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zen ce->__tostring = fe; } else if (zend_string_equals_literal(mname, ZEND_DEBUGINFO_FUNC_NAME)) { ce->__debugInfo = fe; - } else if (ZSTR_LEN(ce->name) == ZSTR_LEN(mname)) { - zend_string *lowercase_name = zend_string_tolower(ce->name); - lowercase_name = zend_new_interned_string(lowercase_name); - if (!memcmp(ZSTR_VAL(mname), ZSTR_VAL(lowercase_name), ZSTR_LEN(mname))) { - if (ce->constructor && (!ce->parent || ce->constructor != ce->parent->constructor)) { - zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ZSTR_VAL(ce->name)); - } - ce->constructor = fe; - fe->common.fn_flags |= ZEND_ACC_CTOR; - } - zend_string_release_ex(lowercase_name, 0); } } /* }}} */ @@ -1569,13 +1592,11 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s if ((existing_fn = zend_hash_find_ptr(*overridden, key)) != NULL) { if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { /* Make sure the trait method is compatible with previosly declared abstract method */ - perform_delayable_implementation_check( - ce, fn, existing_fn, /*always_error*/ 1); + perform_delayable_implementation_check(ce, fn, existing_fn); } if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { /* Make sure the abstract declaration is compatible with previous declaration */ - perform_delayable_implementation_check( - ce, existing_fn, fn, /*always_error*/ 1); + perform_delayable_implementation_check(ce, existing_fn, fn); return; } } @@ -1588,12 +1609,10 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s } else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT && (existing_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0) { /* Make sure the trait method is compatible with previosly declared abstract method */ - perform_delayable_implementation_check( - ce, fn, existing_fn, /*always_error*/ 1); + perform_delayable_implementation_check(ce, fn, existing_fn); } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { /* Make sure the abstract declaration is compatible with previous declaration */ - perform_delayable_implementation_check( - ce, existing_fn, fn, /*always_error*/ 1); + perform_delayable_implementation_check(ce, existing_fn, fn); return; } else if (UNEXPECTED(existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT)) { /* two traits can't define the same non-abstract method */ @@ -2029,9 +2048,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent Z_TRY_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; - if (ZEND_TYPE_IS_NAME(property_info->type)) { - zend_string_addref(ZEND_TYPE_NAME(property_info->type)); - } + zend_type_copy_ctor(&property_info->type, /* persistent */ 0); zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type); zend_string_release_ex(prop_name, 0); } ZEND_HASH_FOREACH_END(); @@ -2141,32 +2158,6 @@ static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */ zend_do_traits_property_binding(ce, traits); efree(traits); - - /* Emit E_DEPRECATED for PHP 4 constructors */ - zend_check_deprecated_constructor(ce); -} -/* }}} */ - - -static zend_bool zend_has_deprecated_constructor(const zend_class_entry *ce) /* {{{ */ -{ - const zend_string *constructor_name; - if (!ce->constructor) { - return 0; - } - constructor_name = ce->constructor->common.function_name; - return !zend_binary_strcasecmp( - ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), - ZSTR_VAL(constructor_name), ZSTR_LEN(constructor_name) - ); -} -/* }}} */ - -void zend_check_deprecated_constructor(const zend_class_entry *ce) /* {{{ */ -{ - if (zend_has_deprecated_constructor(ce)) { - zend_error(E_DEPRECATED, "Methods with the same name as their class will not be constructors in a future version of PHP; %s has a deprecated constructor", ZSTR_VAL(ce->name)); - } } /* }}} */ @@ -2242,7 +2233,6 @@ typedef struct { struct { const zend_function *parent_fn; const zend_function *child_fn; - zend_bool always_error; }; struct { const zend_property_info *parent_prop; @@ -2290,14 +2280,12 @@ 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_bool always_error) { + zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn) { HashTable *obligations = get_or_init_obligations_for_class(ce); variance_obligation *obligation = emalloc(sizeof(variance_obligation)); obligation->type = OBLIGATION_COMPATIBILITY; obligation->child_fn = child_fn; obligation->parent_fn = parent_fn; - obligation->always_error = always_error; zend_hash_next_index_insert_ptr(obligations, obligation); } @@ -2325,18 +2313,14 @@ static int check_variance_obligation(zval *zv) { return ZEND_HASH_APPLY_KEEP; } } else if (obligation->type == OBLIGATION_COMPATIBILITY) { - zend_string *unresolved_class; inheritance_status status = zend_do_perform_implementation_check( - &unresolved_class, obligation->child_fn, obligation->parent_fn); - + obligation->child_fn, obligation->parent_fn); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { return ZEND_HASH_APPLY_KEEP; } ZEND_ASSERT(status == INHERITANCE_ERROR); - emit_incompatible_method_error_or_warning( - obligation->child_fn, obligation->parent_fn, status, unresolved_class, - obligation->always_error); + emit_incompatible_method_error(obligation->child_fn, obligation->parent_fn, status); } /* Either the compatibility check was successful or only threw a warning. */ } else { @@ -2399,17 +2383,14 @@ static void report_variance_errors(zend_class_entry *ce) { ZEND_ASSERT(obligations != NULL); ZEND_HASH_FOREACH_PTR(obligations, obligation) { - inheritance_status status; - zend_string *unresolved_class; - if (obligation->type == OBLIGATION_COMPATIBILITY) { - /* Just used to fetch the unresolved_class in this case. */ - status = zend_do_perform_implementation_check( - &unresolved_class, obligation->child_fn, obligation->parent_fn); + /* 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); ZEND_ASSERT(status == INHERITANCE_UNRESOLVED); - emit_incompatible_method_error_or_warning( - obligation->child_fn, obligation->parent_fn, - status, unresolved_class, obligation->always_error); + emit_incompatible_method_error( + obligation->child_fn, obligation->parent_fn, status); } else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) { emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop); } else { @@ -2522,7 +2503,6 @@ ZEND_API int zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_nam /* Check whether early binding is prevented due to unresolved types in inheritance checks. */ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */ { - inheritance_status ret = INHERITANCE_SUCCESS; zend_string *key; zend_function *parent_func; zend_property_info *parent_info; @@ -2535,11 +2515,7 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e do_inheritance_check_on_method_ex(child_func, parent_func, ce, NULL, 1, 0); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { - if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { - return INHERITANCE_UNRESOLVED; - } - ZEND_ASSERT(status == INHERITANCE_ERROR); - ret = INHERITANCE_ERROR; + return status; } } } ZEND_HASH_FOREACH_END(); @@ -2556,17 +2532,13 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e if (ZEND_TYPE_IS_SET(child_info->type)) { inheritance_status status = property_types_compatible(parent_info, child_info); if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { - if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { - return INHERITANCE_UNRESOLVED; - } - ZEND_ASSERT(status == INHERITANCE_ERROR); - ret = INHERITANCE_ERROR; + return status; } } } } ZEND_HASH_FOREACH_END(); - return ret; + return INHERITANCE_SUCCESS; } /* }}} */ |