Skip to content

Commit ed3b360

Browse files
committed
Make sure that abstract private is implemented in same class
Normally it's enough to mark the class as "abstract" if an abstract method was not implemented. However, if there are abstract private methods, then even they really must be implemented, or it must be forwarded to a method with higher visibility.
1 parent bec80b3 commit ed3b360

File tree

3 files changed

+67
-17
lines changed

3 files changed

+67
-17
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Abstract private trait method not implemented
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
abstract private function method(int $a, string $b);
8+
}
9+
10+
abstract class C {
11+
use T;
12+
}
13+
14+
class D extends C {
15+
private function method(int $a, string $b) {}
16+
}
17+
18+
?>
19+
--EXPECTF--
20+
Fatal error: Class C must implement 1 abstract private method (C::method) in %s on line %d
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Abstract private trait method forwarded as abstract protected method
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
abstract private function method(int $a, string $b);
8+
}
9+
10+
abstract class C {
11+
use T;
12+
13+
abstract protected function method(int $a, string $b);
14+
}
15+
16+
class D extends C {
17+
protected function method(int $a, string $b) {}
18+
}
19+
20+
?>
21+
===DONE===
22+
--EXPECT--
23+
===DONE===

Zend/zend_inheritance.c

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,20 +2122,18 @@ typedef struct _zend_abstract_info {
21222122

21232123
static void zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai) /* {{{ */
21242124
{
2125-
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
2126-
if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
2127-
ai->afn[ai->cnt] = fn;
2128-
}
2129-
if (fn->common.fn_flags & ZEND_ACC_CTOR) {
2130-
if (!ai->ctor) {
2131-
ai->cnt++;
2132-
ai->ctor = 1;
2133-
} else {
2134-
ai->afn[ai->cnt] = NULL;
2135-
}
2136-
} else {
2125+
if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
2126+
ai->afn[ai->cnt] = fn;
2127+
}
2128+
if (fn->common.fn_flags & ZEND_ACC_CTOR) {
2129+
if (!ai->ctor) {
21372130
ai->cnt++;
2131+
ai->ctor = 1;
2132+
} else {
2133+
ai->afn[ai->cnt] = NULL;
21382134
}
2135+
} else {
2136+
ai->cnt++;
21392137
}
21402138
}
21412139
/* }}} */
@@ -2144,16 +2142,23 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
21442142
{
21452143
zend_function *func;
21462144
zend_abstract_info ai;
2147-
2148-
ZEND_ASSERT((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS);
2145+
zend_bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
21492146
memset(&ai, 0, sizeof(ai));
21502147

21512148
ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
2152-
zend_verify_abstract_class_function(func, &ai);
2149+
if (func->common.fn_flags & ZEND_ACC_ABSTRACT) {
2150+
/* If the class is explicitly abstract, we only check private abstract methods,
2151+
* because only they must be declared in the same class. */
2152+
if (!is_explicit_abstract || (func->common.fn_flags & ZEND_ACC_PRIVATE)) {
2153+
zend_verify_abstract_class_function(func, &ai);
2154+
}
2155+
}
21532156
} ZEND_HASH_FOREACH_END();
21542157

21552158
if (ai.cnt) {
2156-
zend_error_noreturn(E_ERROR, "Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")",
2159+
zend_error_noreturn(E_ERROR, !is_explicit_abstract
2160+
? "Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")"
2161+
: "Class %s must implement %d abstract private method%s (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")",
21572162
ZSTR_VAL(ce->name), ai.cnt,
21582163
ai.cnt > 1 ? "s" : "",
21592164
DISPLAY_ABSTRACT_FN(0),
@@ -2432,7 +2437,9 @@ ZEND_API int zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_nam
24322437
if (interfaces) {
24332438
zend_do_implement_interfaces(ce, interfaces);
24342439
}
2435-
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
2440+
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT))
2441+
&& (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
2442+
) {
24362443
zend_verify_abstract_class(ce);
24372444
}
24382445

0 commit comments

Comments
 (0)