I always thought I understood how OOP works (and I have been using it for years), but sometimes I realize some concepts are still not so clear to me.
I just came across this question about method visibility in PHP. The accepted answer explains that a private method cannot be overridden by a child class in PHP. Okay, that makes sense. However, the example made me think about the internal inheritance mechanism in PHP, and the way $this
behaves on inherited methods.
Consider this code (example from the PHP Manual, also included in the question mentioned above):
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic\n";
}
private function testPrivate() {
echo "Foo::testPrivate\n";
}
}
$myFoo = new foo();
$myFoo->test();
/*
Output:
Bar::testPrivate
Foo::testPublic
*/
Now consider this excerpt from the PHP Manual:
The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
The explanation states that "$this
is a reference to the calling object", which is $myFoo
. So I expected that $myFoo->test()
would always invoke Foo::testPrivate
, and never Bar::testPrivate
(unless $myFoo
were an instance of Bar
). I tested $this
with get_class
, and it always returns Foo
, even from inside Bar::testPrivate
and Bar::test
. However, $this
behaves like an instance of Bar
when Bar::test
calls $this->testPrivate()
.
That's really confusing, and I am trying to understand why it works that way!
I thought inherited methods (public
or protected
) were somehow copied from the base to the child class. Private methods would not be copied at all. But this example indicates that it doesn't work like this. It looks like the instance of Foo
keeps an internal instance of Bar
, and delegates method calls to it when necessary.
I am trying to learn something here, and I only learn when things make sense to me. This one does not. After writing all this, I think I can summarize it with two questions:
Could someone briefly explain how inheritance works internally in PHP? Or at least point me to an article or documentation about that?
Is the behavior or
$this
discussed here present on other OO languages as well, or is it particular to PHP?
Inheritance in PHP works the same way it does in most object-oriented languages.
When you have a "virtual" method, the method is not bound directly to the caller. Instead, every class contains a little lookup table which says "this method name is bound to that implementation". So, when you say
$this->testPublic()
, what actually happens is that PHP:testPublic
in that tableSince
Foo
overridestestPublic
, its virtual table contains an entry fortestPublic
pointing toFoo::testPublic
.Now, with the private methods, the behavior is different. Since, as you correctly read, private methods cannot be overridden, calling a private method never results in a virtual table lookup. That is to say, private methods cannot be virtual and must always be defined in the class which uses them.
So, the effect is that the name is bound at the time of declaration: all
Foo
methods will callFoo::testPrivate
when they say$this->testPrivate
, and allBar
methods will callBar::testPrivate
.To sum up, saying that "inherited methods are copied to the child" is not correct. What actually happens is that the child begins with its method-name-lookup-table being populated with its parent class' entries, and then adds its own functions and replaces any overridden entries. When you call
$this->something
, this lookup table is consulted for the current object's class. So if$this
is an instance ofFoo
, andFoo
overridestestPublic
, you getFoo::testPublic
. If$this
is an instance ofBar
, you will getBar::testPublic
.Well,
private
methods and properties are exactly that - private. For all intents and purposes, you can consider them "internal", meaning internal to the class they're defined in. This means that they're never inherited, and can never be overridden.Thus, when using
$this
in combination with aprivate
method or property, it will always be the method or property within the same class as the reference to$this
. This happens because$this
called within a parent class cannot accessprivate
methods or properties in another class (because they're private), even from child classes.Hope this helps.