diff --git a/NEWS b/NEWS index 4364d69650e5..e75fc00dba0b 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0beta1 +- Core: + . Fixed bug GH-14156 (Inherited private methods incorrectly satisfied abstract + trait requirements). (Matthias Görgens) + - GMP: . Added optional $definitely_prime output parameter to gmp_prevprime(). (Weilin Du) diff --git a/Zend/tests/traits/gh14156.phpt b/Zend/tests/traits/gh14156.phpt new file mode 100644 index 000000000000..464443622404 --- /dev/null +++ b/Zend/tests/traits/gh14156.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-14156 (Inherited private method does not satisfy abstract trait requirement) +--FILE-- + +--EXPECTF-- +Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining method (C::test) in %s on line %d diff --git a/Zend/tests/traits/gh14156_2.phpt b/Zend/tests/traits/gh14156_2.phpt new file mode 100644 index 000000000000..7045c9c71f21 --- /dev/null +++ b/Zend/tests/traits/gh14156_2.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-14156 (Abstract trait requirement replaces inherited private method) +--FILE-- +test(); + } +} + +class P { + private function test(): void {} +} + +abstract class C extends P { + use T; +} + +$method = new ReflectionMethod(C::class, 'test'); +var_dump($method->isAbstract()); +var_dump($method->getDeclaringClass()->getName()); + +class D extends C { + public function test(): void { + echo "implemented\n"; + } +} + +(new D())->run(); +?> +--EXPECT-- +bool(true) +string(1) "C" +implemented diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 4424c9a1a3ab..94e18f780828 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2377,8 +2377,12 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ return; } - /* Abstract method signatures from the trait must be satisfied. */ - if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + /* Abstract method signatures from the trait must be satisfied. An inherited + * private method is not accessible from the using class, so it does not + * satisfy the requirement; only a private method from the class itself does. */ + if ((fn->common.fn_flags & ZEND_ACC_ABSTRACT) + && (!(existing_fn->common.fn_flags & ZEND_ACC_PRIVATE) + || fixup_trait_scope(existing_fn, ce) == ce)) { /* "abstract private" methods in traits were not available prior to PHP 8. * As such, "abstract protected" was sometimes used to indicate trait requirements, * even though the "implementing" method was private. Do not check visibility