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.c157
1 files changed, 102 insertions, 55 deletions
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 00c514259b..47f6eb2e0d 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -167,6 +167,20 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
}
/* }}} */
+static zend_always_inline zend_bool zend_iterable_compatibility_check(zend_arg_info *arg_info) /* {{{ */
+{
+ if (arg_info->type_hint == IS_ARRAY) {
+ return 1;
+ }
+
+ if (arg_info->class_name && zend_string_equals_literal_ci(arg_info->class_name, "Traversable")) {
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
{
if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) {
@@ -314,18 +328,25 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
} else {
proto_arg_info = &proto->common.arg_info[proto->common.num_args];
}
-
+
if (!zend_do_perform_type_hint_check(fe, fe_arg_info, proto, proto_arg_info)) {
- return 0;
+ switch (fe_arg_info->type_hint) {
+ case IS_ITERABLE:
+ if (!zend_iterable_compatibility_check(proto_arg_info)) {
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
}
-#if 0
// This introduces BC break described at https://bugs.php.net/bug.php?id=72119
if (proto_arg_info->type_hint && proto_arg_info->allow_null && !fe_arg_info->allow_null) {
/* incompatible nullability */
return 0;
}
-#endif
/* by-ref constraints on arguments are invariant */
if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) {
@@ -340,8 +361,21 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
if (!(fe->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
return 0;
}
-
+
if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) {
+ switch (proto->common.arg_info[-1].type_hint) {
+ case IS_ITERABLE:
+ if (!zend_iterable_compatibility_check(fe->common.arg_info - 1)) {
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ }
+
+ if (fe->common.arg_info[-1].allow_null && !proto->common.arg_info[-1].allow_null) {
return 0;
}
}
@@ -351,6 +385,11 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */
{
+
+ if (arg_info->type_hint != IS_UNDEF && arg_info->allow_null) {
+ smart_str_appendc(str, '?');
+ }
+
if (arg_info->class_name) {
const char *class_name;
size_t class_name_len;
@@ -491,8 +530,6 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function
} else {
smart_str_appends(&str, "NULL");
}
- } else if (arg_info->type_hint && arg_info->allow_null) {
- smart_str_appends(&str, " = NULL");
}
if (++i < num_args) {
@@ -549,17 +586,17 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child));
}
+ /* Prevent derived classes from restricting access that was available in parent classes */
+ if (UNEXPECTED((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
+ }
+
+ if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
+ && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
+ child->common.fn_flags |= ZEND_ACC_CHANGED;
+ }
if (parent_flags & ZEND_ACC_CHANGED) {
child->common.fn_flags |= ZEND_ACC_CHANGED;
- } else {
- /* Prevent derived classes from restricting access that was available in parent classes
- */
- if (UNEXPECTED((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) {
- zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
- } else if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
- && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
- child->common.fn_flags |= ZEND_ACC_CHANGED;
- }
}
if (parent_flags & ZEND_ACC_PRIVATE) {
@@ -590,7 +627,8 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
error_verb = "must";
} else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
(!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1))) {
+ !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) ||
+ (child->common.arg_info[-1].allow_null && !parent->common.arg_info[-1].allow_null))) {
error_level = E_COMPILE_ERROR;
error_verb = "must";
} else {
@@ -660,7 +698,7 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
int parent_num = OBJ_PROP_TO_NUM(parent_info->offset);
int child_num = OBJ_PROP_TO_NUM(child_info->offset);
- /* Don't keep default properties in GC (thry may be freed by opcache) */
+ /* Don't keep default properties in GC (they may be freed by opcache) */
zval_ptr_dtor_nogc(&(ce->default_properties_table[parent_num]));
ce->default_properties_table[parent_num] = ce->default_properties_table[child_num];
ZVAL_UNDEF(&ce->default_properties_table[child_num]);
@@ -736,21 +774,29 @@ ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_
}
/* }}} */
-static void do_inherit_class_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
+static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */
{
- if (!zend_hash_exists(&ce->constants_table, name)) {
- if (!Z_ISREF_P(zv)) {
- if (parent_ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_NEW_PERSISTENT_REF(zv, zv);
- } else {
- ZVAL_NEW_REF(zv, zv);
- }
+ zend_class_constant *c = zend_hash_find_ptr(&ce->constants_table, name);
+
+ if (c != NULL) {
+ if (UNEXPECTED((Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PPP_MASK) > (Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PPP_MASK))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s",
+ ZSTR_VAL(ce->name), ZSTR_VAL(name), zend_visibility_string(Z_ACCESS_FLAGS(parent_const->value)), ZSTR_VAL(ce->parent->name), (Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PUBLIC) ? "" : " or weaker");
}
- if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
+ } else if (!(Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PRIVATE)) {
+ if (Z_CONSTANT(parent_const->value)) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- Z_ADDREF_P(zv);
- _zend_hash_append(&ce->constants_table, name, zv);
+ if (ce->type & ZEND_INTERNAL_CLASS) {
+ if (Z_REFCOUNTED(parent_const->value)) {
+ Z_ADDREF(parent_const->value);
+ }
+ c = pemalloc(sizeof(zend_class_constant), 1);
+ memcpy(c, parent_const, sizeof(zend_class_constant));
+ } else {
+ c = parent_const;
+ }
+ _zend_hash_append_ptr(&ce->constants_table, name, c);
}
}
/* }}} */
@@ -760,7 +806,6 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
zend_property_info *property_info;
zend_function *func;
zend_string *key;
- zval *zv;
if (UNEXPECTED(ce->ce_flags & ZEND_ACC_INTERFACE)) {
/* Interface can only inherit other interfaces */
@@ -897,12 +942,14 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
}
if (zend_hash_num_elements(&parent_ce->constants_table)) {
+ zend_class_constant *c;
+
zend_hash_extend(&ce->constants_table,
zend_hash_num_elements(&ce->constants_table) +
zend_hash_num_elements(&parent_ce->constants_table), 0);
- ZEND_HASH_FOREACH_STR_KEY_VAL(&parent_ce->constants_table, key, zv) {
- do_inherit_class_constant(key, zv, ce, parent_ce);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->constants_table, key, c) {
+ do_inherit_class_constant(key, c, ce);
} ZEND_HASH_FOREACH_END();
}
@@ -932,14 +979,12 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
}
/* }}} */
-static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zval *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
+static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
{
- zval *old_constant;
+ zend_class_constant *old_constant;
- if ((old_constant = zend_hash_find(child_constants_table, name)) != NULL) {
- if (!Z_ISREF_P(old_constant) ||
- !Z_ISREF_P(parent_constant) ||
- Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) {
+ if ((old_constant = zend_hash_find_ptr(child_constants_table, name)) != NULL) {
+ if (old_constant->ce != parent_constant->ce) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", ZSTR_VAL(name), ZSTR_VAL(iface->name));
}
return 0;
@@ -948,21 +993,23 @@ static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zva
}
/* }}} */
-static void do_inherit_iface_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */
+static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */
{
- if (do_inherit_constant_check(&ce->constants_table, zv, name, iface)) {
- if (!Z_ISREF_P(zv)) {
- if (iface->type == ZEND_INTERNAL_CLASS) {
- ZVAL_NEW_PERSISTENT_REF(zv, zv);
- } else {
- ZVAL_NEW_REF(zv, zv);
- }
- }
- Z_ADDREF_P(zv);
- if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
+ if (do_inherit_constant_check(&ce->constants_table, c, name, iface)) {
+ zend_class_constant *ct;
+ if (Z_CONSTANT(c->value)) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- zend_hash_update(&ce->constants_table, name, zv);
+ if (ce->type & ZEND_INTERNAL_CLASS) {
+ if (Z_REFCOUNTED(c->value)) {
+ Z_ADDREF(c->value);
+ }
+ ct = pemalloc(sizeof(zend_class_constant), 1);
+ memcpy(ct, c, sizeof(zend_class_constant));
+ } else {
+ ct = c;
+ }
+ zend_hash_update_ptr(&ce->constants_table, name, ct);
}
}
/* }}} */
@@ -974,7 +1021,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0;
zend_function *func;
zend_string *key;
- zval *zv;
+ zend_class_constant *c;
for (i = 0; i < ce->num_interfaces; i++) {
if (ce->interfaces[i] == NULL) {
@@ -990,8 +1037,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
if (ignore) {
/* Check for attempt to redeclare interface constants */
- ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, zv) {
- do_inherit_constant_check(&iface->constants_table, zv, key, iface);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ do_inherit_constant_check(&iface->constants_table, c, key, iface);
} ZEND_HASH_FOREACH_END();
} else {
if (ce->num_interfaces >= current_iface_num) {
@@ -1003,8 +1050,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
ce->interfaces[ce->num_interfaces++] = iface;
- ZEND_HASH_FOREACH_STR_KEY_VAL(&iface->constants_table, key, zv) {
- do_inherit_iface_constant(key, zv, ce, iface);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) {
+ do_inherit_iface_constant(key, c, ce, iface);
} ZEND_HASH_FOREACH_END();
ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) {