I have a drawing PHP class called ClassA that is extended by many other drawing classes, ClassB for instance.
I need the inherited classes to fire its parent classes' Draw() method. However, in my particular situation, I do not want to call such method directly (e.g.: parent::Draw()
). I'd like a third function (e.g.: parent::InvokeDraw()
) to call my drawing method from within the parent's context.
Here's some code to illustrate:
class ClassA
{
function Draw()
{
/* Drawing code ... */
}
function InvokeDraw()
{
$this->Draw();
}
}
class ClassB extends ClassA
{
function Draw()
{
parent::InvokeDraw();
/* Drawing code ... */
}
}
The problem I'm facing is that InvokeDraw() will not call the parent's Draw() method, but rather the extended class' own Draw() method, thus causing an infinite loop.
Although the issue is fairly logical, I am having a hard time figuring out a workaround on that. How to accomplish this task?
On a superficial level, inheritance works like merging code. Inherited methods are part of the child class as if they were native to the class:
class A {
public function foo() {
echo 'foo';
}
public function bar() {
echo 'bar';
}
}
class B extends A { }
For all intents and purposes, this is equivalent to writing:
class B {
public function foo() {
echo 'foo';
}
public function bar() {
echo 'bar';
}
}
Class B
has the function foo
and bar
as if they were part of its declaration.
Overriding methods in a child class replaces the one specific implementation with another:
class B extends A {
public function bar() {
echo 'baz';
}
}
This is as if B
was declared like this:
class B {
public function foo() {
echo 'foo';
}
public function bar() {
echo 'baz';
}
}
With one exception: the parent's implementation of a method is available using the parent
keyword. It does not change the context in which the code is executed, is merely uses the parent's implementation of the method:
class B extends A {
public function bar() { public function bar() {
parent::bar(); ---> echo 'bar';
} }
}
It works in the same context of the current instance of B
, it just pulls the old code of the parent out of the ether.
If you call another method in the parent's method, it executes in the context of the child, not in the context of the parent class. Because you have not instantiated the parent, you are working with a child. To demonstrate with properties (it works the same with methods):
class A {
protected $value = 'bar';
public function bar() {
echo $this->value;
}
}
class B extends A {
protected $value = 'baz';
public function bar() {
parent::bar(); // outputs "baz"
echo $this->value; // outputs "baz"
}
}
As such, there is no solution to your problem. You cannot call a method in the "parent's context". You can call code of the parent in the current context, that's all. What you're creating is a simple loop, because it doesn't matter whether the code is inherited or not, it all works in the same context.
You need to redesign that class to not call methods in a loop.
This one is with using static methods
<?php
class ClassA
{
function Draw()
{
echo "DrawA";
/* Drawing code ... */
}
function InvokeDraw()
{
self::Draw();
}
}
class ClassB extends ClassA
{
function Draw()
{
echo "DrawB";
parent::InvokeDraw();
/* Drawing code ... */
}
}
echo "Begin:<br>";
$cb = new ClassB();
$cb->Draw();
Note that the only thing I changed is the InvokeDraw()
method and made it use self
which refers to a class, rather than object as it is with $this
Output:
Begin:
DrawBDrawA
Edit: To answer your comment below I will add a short description of how your code works and how this code works.
What happens in your code:
B
and start working with itB->Draw()
while working within B
class AND B
object.B->Draw()
calls statically(that means class method) A::Invoke()
from class A BUT we are still using B
object.A::Invoke()
calls $this->Draw();
and as we are working currently with B
object, $this
refers to an instance of ClassB
.What happens in the code above:
B
and start working with itB->Draw()
while working within B
class AND B
object.B->Draw()
calls statically(that means class method) A::Invoke
from class A BUT as well as in your code we are still using B
object.A::Invoke()
calls self::Draw()
which is basically the same as ClassA::Draw()
and because it's a static method, we don't care what object we are currently working with and we call the A
's Draw()
method.A::Draw()
method executes as we need.I will provide the same explanation for the code in my second answer, which doesn't use static calls:
B
and start working with itB->Draw()
while working within B
class AND B
object.B->Draw()
CREATES an instance of A
.B->Draw()
calls A->Invoke()
which means we start to work with an object that is instance of class A
and not B
like before.At this point we completely forget that B
even exists and work only with A
A->Invoke()
calls $this->Draw()
which means that we are calling A->Draw()
, because we already work with an instance of class A
.A->Draw()
executes as we expect.From usability point of view, we can see that the static method is better, as we can define some static properties and you can work with them when A::Draw()
executes. If we use non-static method, then we need to pass the data we need within arguments of our methods.
I hope this makes it clear. The sequences above do not have the right terminology, but it was written on purpose, I think it's easier to understand the flow that way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With