Skip to content

Commit 232a960

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

8 files changed

+116
-6
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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6091,6 +6091,10 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
60916091

60926092
zend_string *lcname;
60936093

6094+
if ((fn_flags & ZEND_ACC_PRIVATE) && (fn_flags & ZEND_ACC_FINAL) && !zend_is_constructor(name)) {
6095+
zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes");
6096+
}
6097+
60946098
if (in_interface) {
60956099
if (!(fn_flags & ZEND_ACC_PUBLIC) || (fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_ABSTRACT))) {
60966100
zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "

Zend/zend_inheritance.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,14 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z
810810
uint32_t parent_flags = parent->common.fn_flags;
811811
zend_function *proto;
812812

813+
if (UNEXPECTED((parent_flags & ZEND_ACC_PRIVATE) && !(parent_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_CTOR))) {
814+
if (!check_only) {
815+
child->common.fn_flags |= ZEND_ACC_CHANGED;
816+
}
817+
/* The parent method is private and not an abstract so we don't need to check any inheritance rules */
818+
return INHERITANCE_SUCCESS;
819+
}
820+
813821
if (!checked && UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
814822
if (check_only) {
815823
return INHERITANCE_ERROR;
@@ -851,10 +859,6 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z
851859
child->common.fn_flags |= ZEND_ACC_CHANGED;
852860
}
853861

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

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/final_private_ctor.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Final private constructors cannot be overridden
3+
--FILE--
4+
<?php
5+
6+
class Base
7+
{
8+
private final function __construct()
9+
{
10+
}
11+
}
12+
class Extended extends Base
13+
{
14+
public function __construct()
15+
{
16+
}
17+
}
18+
19+
?>
20+
--EXPECTF--
21+
Fatal error: Cannot override final method Base::__construct() in %s on line %d

tests/classes/inheritance_007.phpt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
Ensure private methods with the same name are not checked for inheritance rules - final
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+
?>
40+
--EXPECTF--
41+
Warning: Private methods cannot be final as they are never overridden by other classes %s
42+
43+
Warning: Private methods cannot be final as they are never overridden by other classes %s
44+
A::normalPrivate
45+
A::finalPrivate
46+
A::normalPrivate
47+
A::finalPrivate
48+
B::normalPrivate
49+
B::finalPrivate
50+
A::normalPrivate
51+
A::finalPrivate

tests/classes/inheritance_008.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Ensure private methods with the same name are not checked for inheritance rules - static
3+
--FILE--
4+
<?php
5+
class A {
6+
static private function foo() { }
7+
private function bar() {}
8+
}
9+
class B extends A {
10+
private function foo() {}
11+
static private function bar() {}
12+
}
13+
echo 'OK';
14+
?>
15+
--EXPECT--
16+
OK

tests/classes/inheritance_009.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Ensure private methods with the same name are not checked for inheritance rules - abstract
3+
--FILE--
4+
<?php
5+
class A {
6+
private function test() {}
7+
}
8+
abstract class B extends A {
9+
abstract function test();
10+
}
11+
echo 'OK';
12+
?>
13+
--EXPECT--
14+
OK

0 commit comments

Comments
 (0)