Skip to content

Commit d25f863

Browse files
committed
Ignore inheritance rules on private methods
1 parent add8c15 commit d25f863

File tree

5 files changed

+67
-7
lines changed

5 files changed

+67
-7
lines changed

Zend/tests/method_argument_binding.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class C extends B {
2424
(new C)->test();
2525

2626
class D {
27-
private final function method(&$x) {
27+
private function method(&$x) {
2828
++$x;
2929
}
3030
}

Zend/zend_compile.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,9 @@ uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
848848
"Cannot use the final modifier on an abstract class member", 0);
849849
return 0;
850850
}
851+
if ((new_flags & ZEND_ACC_PRIVATE) && (new_flags & ZEND_ACC_FINAL)) {
852+
zend_error(E_COMPILE_WARNING, "Private methods are inherently final as they cannot be overridden by other classes");
853+
}
851854
return new_flags;
852855
}
853856
/* }}} */

Zend/zend_inheritance.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */
179179
}
180180

181181
if (ce->constructor) {
182-
if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) {
182+
if (parent->constructor
183+
&& !(parent->constructor->common.fn_flags & ZEND_ACC_PRIVATE)
184+
&& UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) {
183185
zend_error_noreturn(E_ERROR, "Cannot override final %s::%s() with %s::%s()",
184186
ZSTR_VAL(parent->name), ZSTR_VAL(parent->constructor->common.function_name),
185187
ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
@@ -810,6 +812,15 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z
810812
uint32_t parent_flags = parent->common.fn_flags;
811813
zend_function *proto;
812814

815+
if (UNEXPECTED((parent_flags & ZEND_ACC_PRIVATE) && !(parent_flags & ZEND_ACC_ABSTRACT))) {
816+
if (!check_only) {
817+
child->common.fn_flags |= ZEND_ACC_CHANGED;
818+
child->common.prototype = NULL;
819+
}
820+
/* The parent method is private and not an abstract so we don't need to check any inheritance rules */
821+
return INHERITANCE_SUCCESS;
822+
}
823+
813824
if (!checked && UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
814825
if (check_only) {
815826
return INHERITANCE_ERROR;
@@ -851,10 +862,6 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z
851862
child->common.fn_flags |= ZEND_ACC_CHANGED;
852863
}
853864

854-
if ((parent_flags & ZEND_ACC_PRIVATE) && !(parent_flags & ZEND_ACC_ABSTRACT)) {
855-
return INHERITANCE_SUCCESS;
856-
}
857-
858865
proto = parent->common.prototype ?
859866
parent->common.prototype : parent;
860867

tests/classes/clone_005.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ abstract class base {
66
public $a = 'base';
77

88
// disallow cloning once forever
9-
final private function __clone() {}
9+
final protected function __clone() {}
1010
}
1111

1212
class test extends base {

tests/classes/inheritance_007.phpt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
Ensure private methods with the same name are not checked for inheritance rules
3+
--FILE--
4+
<?php
5+
class A {
6+
function callYourPrivates() {
7+
$this->normalPrivate();
8+
$this->finalPrivate();
9+
}
10+
function notOverridden_callYourPrivates() {
11+
$this->normalPrivate();
12+
$this->finalPrivate();
13+
}
14+
private function normalPrivate() {
15+
echo __METHOD__ . PHP_EOL;
16+
}
17+
final private function finalPrivate() {
18+
echo __METHOD__ . PHP_EOL;
19+
}
20+
}
21+
class B extends A {
22+
function callYourPrivates() {
23+
$this->normalPrivate();
24+
$this->finalPrivate();
25+
}
26+
private function normalPrivate() {
27+
echo __METHOD__ . PHP_EOL;
28+
}
29+
final private function finalPrivate() {
30+
echo __METHOD__ . PHP_EOL;
31+
}
32+
}
33+
$a = new A();
34+
$a->callYourPrivates();
35+
$a->notOverridden_callYourPrivates();
36+
$b = new B();
37+
$b->callYourPrivates();
38+
$b->notOverridden_callYourPrivates();
39+
--EXPECTF--
40+
Warning: Private methods are inherently final as they cannot be overridden by other classes %s
41+
42+
Warning: Private methods are inherently final as they cannot be overridden by other classes %s
43+
A::normalPrivate
44+
A::finalPrivate
45+
A::normalPrivate
46+
A::finalPrivate
47+
B::normalPrivate
48+
B::finalPrivate
49+
A::normalPrivate
50+
A::finalPrivate

0 commit comments

Comments
 (0)