Consider the following piece of code:
class foo {
private function m() {
echo 'foo->m() ';
}
public function call() {
$this->m();
}
}
class bar extends foo {
private function m() {
echo 'bar->m() ';
}
public function callbar() {
$this->m();
}
}
$bar = new bar;
$bar->call();
$bar->callbar();
Now, changing the visibility of the m()
method, I get:
(+
for public
, -
for private
)
Visibility bar->call() bar->callbar()
======================================================
-foo->m(), -bar->m() foo->m() bar->m()
-foo->m(), +bar->m() foo->m() bar->m()
+foo->m(), -bar->m() ERROR ERROR
+foo->m(), +bar->m() bar->m() bar->m()
(protected
seems to behave like public
).
I was expecting everything to behave like it does when both are declared public
. But although foo->call()
and bar->callbar()
are essentially the same thing, they yield different results depending on the visibility of m()
in foo
and bar
. Why does this happen?
In PHP, methods (including private ones) in the subclasses are either:
You can see this with this code:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
//not necessary; just to make explicit what's happening
function callH() { parent::callH(); }
}
$b = new B;
$b->callH();
Now if you override the private method, its new scope will not be A, it will be B, and the call will fail because A::callH()
runs in scope A
:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'
Here the rules are as follows:
bar
).
bar->call()
, the scope of call
is foo
. Calling $this->m()
elicits a lookup in the method table of bar
for m
, yielding a private bar::m()
. However, the scope of bar::m()
is different from the calling scope, which foo
. The method foo:m()
is found when traversing up the hierarchy and is used instead.foo
, public in bar
) The scope of call
is still foo
. The lookup yields a public bar::m()
. However, its scope is marked as having changed, so a lookup is made in the function table of the calling scope foo
for method m()
. This yields a private method foo:m()
with the same scope as the calling scope, so it's used instead.call
is still foo
. The lookup yields a public bar::m()
. Its scope isn't marked as having changed (they're both public), so bar::m()
is used.