Skip to content

Commit 4e2a249

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 576baec commit 4e2a249

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
@@ -2128,20 +2128,18 @@ typedef struct _zend_abstract_info {
21282128

21292129
static void zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai) /* {{{ */
21302130
{
2131-
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
2132-
if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
2133-
ai->afn[ai->cnt] = fn;
2134-
}
2135-
if (fn->common.fn_flags & ZEND_ACC_CTOR) {
2136-
if (!ai->ctor) {
2137-
ai->cnt++;
2138-
ai->ctor = 1;
2139-
} else {
2140-
ai->afn[ai->cnt] = NULL;
2141-
}
2142-
} else {
2131+
if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
2132+
ai->afn[ai->cnt] = fn;
2133+
}
2134+
if (fn->common.fn_flags & ZEND_ACC_CTOR) {
2135+
if (!ai->ctor) {
21432136
ai->cnt++;
2137+
ai->ctor = 1;
2138+
} else {
2139+
ai->afn[ai->cnt] = NULL;
21442140
}
2141+
} else {
2142+
ai->cnt++;
21452143
}
21462144
}
21472145
/* }}} */
@@ -2150,16 +2148,23 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
21502148
{
21512149
zend_function *func;
21522150
zend_abstract_info ai;
2153-
2154-
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);
2151+
zend_bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
21552152
memset(&ai, 0, sizeof(ai));
21562153

21572154
ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
2158-
zend_verify_abstract_class_function(func, &ai);
2155+
if (func->common.fn_flags & ZEND_ACC_ABSTRACT) {
2156+
/* If the class is explicitly abstract, we only check private abstract methods,
2157+
* because only they must be declared in the same class. */
2158+
if (!is_explicit_abstract || (func->common.fn_flags & ZEND_ACC_PRIVATE)) {
2159+
zend_verify_abstract_class_function(func, &ai);
2160+
}
2161+
}
21592162
} ZEND_HASH_FOREACH_END();
21602163

21612164
if (ai.cnt) {
2162-
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 ")",
2165+
zend_error_noreturn(E_ERROR, !is_explicit_abstract
2166+
? "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 ")"
2167+
: "Class %s must implement %d abstract private method%s (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")",
21632168
ZSTR_VAL(ce->name), ai.cnt,
21642169
ai.cnt > 1 ? "s" : "",
21652170
DISPLAY_ABSTRACT_FN(0),
@@ -2438,7 +2443,9 @@ ZEND_API int zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_nam
24382443
if (interfaces) {
24392444
zend_do_implement_interfaces(ce, interfaces);
24402445
}
2441-
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) {
2446+
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT))
2447+
&& (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
2448+
) {
24422449
zend_verify_abstract_class(ce);
24432450
}
24442451

0 commit comments

Comments
 (0)