summaryrefslogtreecommitdiff
path: root/Zend/zend_inheritance.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_inheritance.c')
-rw-r--r--Zend/zend_inheritance.c494
1 files changed, 233 insertions, 261 deletions
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 56d9e6268f..1a27bbdfb6 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),
ZSTR_VAL(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;
}
/* }}} */