diff --git a/Zend/tests/traits/bugs/gh13177.phpt b/Zend/tests/traits/bugs/gh13177.phpt new file mode 100644 index 0000000000000..42ef0ae9d60d7 --- /dev/null +++ b/Zend/tests/traits/bugs/gh13177.phpt @@ -0,0 +1,63 @@ +--TEST-- +GH-13177 (PHP 8.3.2: final private constructor not allowed when used in trait) +--FILE-- +getMethod("__construct"), "\n"; +} + +class Foo5 extends Foo3 { + private function __construct() {} +} + +?> +--EXPECTF-- +Warning: Private methods cannot be final as they are never overridden by other classes in %s on line %d +Method [ final private method __construct ] { + @@ %sgh13177.php 4 - 4 +} + +Method [ final private method __construct ] { + @@ %sgh13177.php 4 - 4 +} + +Method [ final private method __construct ] { + @@ %sgh13177.php 4 - 4 +} + +Method [ final private method __construct ] { + @@ %sgh13177.php 24 - 24 +} + + +Fatal error: Cannot override final method Foo3::__construct() in %s on line %d diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6362090ada908..ea8cc219a23a3 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1949,10 +1949,6 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ zend_function *new_fn; bool check_inheritance = false; - if ((fn->common.fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) == (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) { - zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes"); - } - if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) { /* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless * of where it is coming from there is no conflict and we do not need to add it again */ @@ -2033,6 +2029,17 @@ static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /* } /* }}} */ +static void zend_traits_check_private_final_inheritance(uint32_t original_fn_flags, zend_function *fn_copy, zend_string *name) +{ + /* If the function was originally already private+final, then it will have already been warned about. + * If the function became private+final only after applying modifiers, we need to emit the same warning. */ + if ((original_fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) != (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL) + && (fn_copy->common.fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) == (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL) + && !zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME)) { + zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes"); + } +} + static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable *exclude_table, zend_class_entry **aliases) /* {{{ */ { zend_trait_alias *alias, **alias_ptr; @@ -2058,6 +2065,8 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z fn_copy.common.fn_flags = alias->modifiers | fn->common.fn_flags; } + zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, alias->alias); + lcname = zend_string_tolower(alias->alias); zend_add_trait_method(ce, alias->alias, lcname, &fn_copy); zend_string_release_ex(lcname, 0); @@ -2095,6 +2104,8 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z } } + zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, fnname); + zend_add_trait_method(ce, fn->common.function_name, fnname, &fn_copy); } }