diff --git a/Zend/tests/gh19466_001.phpt b/Zend/tests/gh19466_001.phpt new file mode 100644 index 0000000000000..1a37afa30090d --- /dev/null +++ b/Zend/tests/gh19466_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/gh19466_002.phpt b/Zend/tests/gh19466_002.phpt new file mode 100644 index 0000000000000..023af5c2d544e --- /dev/null +++ b/Zend/tests/gh19466_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +--EXPECTF-- +Fatal error: T1 and T2 define the same property ($prop1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/gh19466_003.phpt b/Zend/tests/gh19466_003.phpt new file mode 100644 index 0000000000000..dfba329d86576 --- /dev/null +++ b/Zend/tests/gh19466_003.phpt @@ -0,0 +1,26 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/gh19466_004.phpt b/Zend/tests/gh19466_004.phpt new file mode 100644 index 0000000000000..eef7372126f15 --- /dev/null +++ b/Zend/tests/gh19466_004.phpt @@ -0,0 +1,28 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +--EXPECTF-- +Fatal error: T1 and T2 define the same property ($prop1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/gh19466_005.phpt b/Zend/tests/gh19466_005.phpt new file mode 100644 index 0000000000000..39e33d487af1f --- /dev/null +++ b/Zend/tests/gh19466_005.phpt @@ -0,0 +1,25 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +--EXPECTF-- +Fatal error: T1 and T2 define the same property ($prop1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/gh19466_006.phpt b/Zend/tests/gh19466_006.phpt new file mode 100644 index 0000000000000..09ab966f35e6a --- /dev/null +++ b/Zend/tests/gh19466_006.phpt @@ -0,0 +1,26 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/gh19466_007.phpt b/Zend/tests/gh19466_007.phpt new file mode 100644 index 0000000000000..6c336b18a1405 --- /dev/null +++ b/Zend/tests/gh19466_007.phpt @@ -0,0 +1,25 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +--EXPECTF-- +Fatal error: T1 and T2 define the same property ($prop1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/tests/gh19466_008.phpt b/Zend/tests/gh19466_008.phpt new file mode 100644 index 0000000000000..678a043aaa88f --- /dev/null +++ b/Zend/tests/gh19466_008.phpt @@ -0,0 +1,26 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/gh19466_009.phpt b/Zend/tests/gh19466_009.phpt new file mode 100644 index 0000000000000..bbbc1f3d464cc --- /dev/null +++ b/Zend/tests/gh19466_009.phpt @@ -0,0 +1,25 @@ +--TEST-- +Trait property attribute compatibility validation +--FILE-- + +--EXPECTF-- +Fatal error: T1 and T2 define the same property ($prop1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index bbc7a767e1b2f..0859d68a89e76 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -31,6 +31,7 @@ #include "zend_attributes.h" #include "zend_constants.h" #include "zend_observer.h" +#include "zend_call_stack.h" ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; ZEND_API 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) = NULL; @@ -2867,6 +2868,54 @@ static const zend_class_entry* find_first_property_definition(const zend_class_e } /* }}} */ +static bool zend_compare_constant_ast(zend_ast *lhs, zend_ast *rhs) { +#ifdef ZEND_CHECK_STACK_LIMIT + if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) { + zend_call_stack_size_error(); + return true; + } +#endif + + if (lhs->kind != rhs->kind) { + return false; + } + if (lhs->attr != rhs->attr) { + return false; + } + if (lhs->kind == ZEND_AST_ZVAL) { + if (!zend_is_identical(zend_ast_get_zval(lhs), zend_ast_get_zval(rhs))) { + return false; + } + } else if (lhs->kind == ZEND_AST_CONSTANT) { + if (!zend_string_equals(zend_ast_get_constant_name(lhs), zend_ast_get_constant_name(rhs))) { + return false; + } + } else if (zend_ast_is_list(lhs)) { + zend_ast_list *lhs_list = zend_ast_get_list(lhs); + zend_ast_list *rhs_list = zend_ast_get_list(rhs); + if (lhs_list->children != rhs_list->children) { + return false; + } + for (uint32_t i = 0; i < rhs_list->children; i++) { + zend_ast *lhs_child = lhs_list->child[i]; + zend_ast *rhs_child = rhs_list->child[i]; + if (!zend_compare_constant_ast(lhs_child, rhs_child)) { + return false; + } + } + } else { + for (uint32_t i = 0; i < zend_ast_get_num_children(lhs); i++) { + zend_ast *lhs_child = lhs->child[i]; + zend_ast *rhs_child = rhs->child[i]; + if (!zend_compare_constant_ast(lhs_child, rhs_child)) { + return false; + } + } + } + + return true; +} + static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */ { zend_property_info *property_info; @@ -2922,6 +2971,43 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent is_compatible = check_trait_property_or_constant_value_compatibility(ce, op1, op2); } + if ((bool)property_info->attributes != (bool)colliding_prop->attributes) { +attributes_incompatible: + is_compatible = false; + } else if (property_info->attributes) { + if (zend_hash_num_elements(property_info->attributes) != zend_hash_num_elements(colliding_prop->attributes)) { + goto attributes_incompatible; + } + zval *colliding_attr_zv = colliding_prop->attributes->arPacked; + ZEND_HASH_PACKED_FOREACH_PTR(property_info->attributes, zend_attribute *attr) { + zend_attribute *colliding_attr = Z_PTR_P(colliding_attr_zv); + if (!zend_string_equals(attr->lcname, colliding_attr->lcname)) { + goto attributes_incompatible; + } + if (attr->argc != colliding_attr->argc) { + goto attributes_incompatible; + } + for (uint32_t i = 0; i < attr->argc; i++) { + zend_attribute_arg *attr_arg = &attr->args[i]; + zend_attribute_arg *colliding_attr_arg = &colliding_attr->args[i]; + if ((bool)attr_arg->name != (bool)colliding_attr_arg->name) { + goto attributes_incompatible; + } + if (attr_arg->name && !zend_string_equals(attr_arg->name, colliding_attr_arg->name)) { + goto attributes_incompatible; + } + if (Z_TYPE(attr_arg->value) == IS_CONSTANT_AST && Z_TYPE(colliding_attr_arg->value) == IS_CONSTANT_AST) { + if (!zend_compare_constant_ast(Z_ASTVAL(attr_arg->value), Z_ASTVAL(colliding_attr_arg->value))) { + goto attributes_incompatible; + } + } else if (!zend_is_identical(&attr_arg->value, &colliding_attr_arg->value)) { + goto attributes_incompatible; + } + } + colliding_attr_zv++; + } ZEND_HASH_FOREACH_END(); + } + if (!is_compatible) { zend_error_noreturn(E_COMPILE_ERROR, "%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",