I've found something that appears to be a strange inheritance issue in PHP.
Members declared protected can be accessed only within the class itself and by inherited and parent classes.
To me this means:
A can access the protected members of B if A instanceof B
or B instanceof A
.
However, if both A and B extend Foo, and Foo has a protected constructor which is not overwritten in B, then I can create an instance of B from within A. This does not make sense to me, because A is not an instance of B and B is not an instance of A. I can also call the protected method $b->test()
from within A, which executes the method implemented in B. (If B does not redeclare test()
then the implementation in Foo is executed.) To me this is even more strange because I cannot create an instance of B from within A if B directly implements a protected constructor. It seems strange that I cannot access a protected constructor (also declared in the parent class) but accessing a protected method (also declared in the parent class) is no problem.
Note that I do get the expected behavior when I use a class C which does not extend Foo. If I try to instantiate B from within C, I get a fatal error because I'm trying to access a protected constructor. If I add a public constructor to B it is possible to instantiate it (which is expected) and I still cannot access the protected method test()
(this is also expected behavior). I expect the same behavior when using A instead of C.
Sample code which explains again:
class Foo {
protected function __construct() {
echo('Constructing ' . get_called_class());
}
protected function test() {
echo('Hello world ' . __METHOD__);
}
}
class A extends Foo {
public function __construct() {
parent::__construct();
}
public function testB() {
// Both of these lines work
$b = new B();
$b->test();
}
}
class B extends Foo {
protected function test() {
echo('Hello world Again ' . __METHOD__);
}
}
class C {
public function __construct() {
}
public function testB() {
// Both of these lines cause fatal errors
$b = new B();
$b->test();
}
}
$a = new A();
$a->testB();
$c = new C();
$c->testB();
I'm probably not seeing something, but I can't find what. Could anyone explain the behavior to me?
B
the A::testB does not works. So I think that when A::testB try to callB
constructor, it try to call the Foo constructor directly (because it missed inB
). The Foo::_constructor is accessible from A, so it works (maybe it has no memory of child class). I don't tell here if it's right or wrong… but I think that happens this – Luca Rainone