How to call grandparent method without getting E_STRICT error?
Solution 1
You may use ReflectionMethod->invoke()
Example:
<?php
class Grandpa {
protected $age = 'very old';
public function sayMyAge() {
return 'sayMyAge() in Grandpa should be very old. ' .
'My age is: ' . $this->age;
}
}
class Pa extends Grandpa {
protected $age = 'less old';
public function sayMyAge() {
return 'sayMyAge() in Pa should be less old. ' .
'My age is: ' . $this->age;
}
}
class Son extends Pa {
protected $age = 'younger';
public function sayMyAge() {
return 'sayMyAge() in Son should be younger. ' .
'My age is: ' . $this->age;
}
}
$son = new Son();
$reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($son)),
'sayMyAge');
echo $reflectionMethod->invoke($son);
// returns:
// sayMyAge() in Grandpa should be very old. My age is: younger
Note: The invoked method must be public.
Solution 2
You can call the grandparent directly by name (does not need Reflection, nor call_user_func
).
class Base {
protected function getFoo() {
return 'Base';
}
}
class Child extends Base {
protected function getFoo() {
return parent::getFoo() . ' Child';
}
}
class Grandchild extends Child {
protected function getFoo() {
return Base::getFoo() . ' Grandchild';
}
}
The Base::getFoo
call may look like a static call (due to the colon ::
syntax), however it is not. Just like parent::
isn't static, either.
Calling a method from the inheritance chain within a class will correctly bind the $this
value, invoke it as a regular method, honour the visibility rules (e.g. protected), and is not a violation of any kind!
This may look a bit strange at first, but, this is the way to do it in PHP.
Solution 3
Timo's answer works but I think it works by accident more than by design. If you look at the opcodes for a $this->doX vs a parent::doX vs a Grandparent::doX you can see that Grandparent::doX is invoked as a static method but PHP will use the $this that's in scope
$this->doX
17 1 EXT_STMT
2 INIT_METHOD_CALL 'doX'
3 EXT_FCALL_BEGIN
4 DO_FCALL 0
5 EXT_FCALL_END
parent::doX
18 6 EXT_STMT
7 FETCH_CLASS 514 :1
8 INIT_STATIC_METHOD_CALL $1, 'doX'
9 EXT_FCALL_BEGIN
10 DO_FCALL 0
11 EXT_FCALL_END
Grandparent::doX
19 12 EXT_STMT
13 INIT_STATIC_METHOD_CALL 'C1', 'doX'
14 EXT_FCALL_BEGIN
15 DO_FCALL 0
16 EXT_FCALL_END
The $this parameter is available in the Grandparent's static invocation because of this (from https://www.php.net/manual/en/language.oop5.basic.php):
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). As of PHP 7.0.0 calling a non-static method statically from an incompatible context results in $this being undefined inside the method. Calling a non-static method statically from an incompatible context has been deprecated as of PHP 5.6.0. As of PHP 7.0.0 calling a non-static method statically has been generally deprecated (even if called from a compatible context). Before PHP 5.6.0 such calls already triggered a strict notice.
Solution 4
You can use a separate internal method (e.g. _doStuff
to complement doStuff
) and call that directly from the grandchild, through the parent.
// Base class that everything inherits
class Grandpa {
protected function _doStuff() {
// grandpa's logic
echo 'grandpa ';
}
public function doStuff() {
$this->_doStuff();
}
}
class Papa extends Grandpa {
public function doStuff() {
parent::doStuff();
echo 'papa ';
}
}
class Kiddo extends Papa {
public function doStuff() {
// Calls _doStuff instead of doStuff
parent::_doStuff();
echo 'kiddo';
}
}
$person = new Grandpa();
$person->doStuff();
echo "\n";
$person = new Papa();
$person->doStuff();
echo "\n";
$person = new Kiddo();
$person->doStuff();
shows
grandpa
grandpa papa
grandpa kiddo
Related videos on Youtube
Enrique
Updated on September 15, 2022Comments
-
Enrique over 1 year
Sometimes I need to execute grandparent method (that is, bypass the parent method), I know this is code smell, but sometimes I can't change the other classes (frameworks, libraries, etc).
In PHP we can do that with something like:
call_user_func(array(get_parent_class(get_parent_class($childObject)), 'grandParentMethod'));
The problem is that if you have E_STRICT errors enabled you will get an error like:
Strict standards: non-static method GrandParent::grandParentMethod() should not be called statically in ...
I've found only one solution for this (without removing E_STRICT), and it's just adding the
@
to supress the error.But that's really ugly, does somebody know a better solution?
Thanks ! PS: I can't instantiate a new object like:
$grandparent = get_parent_class(get_parent_class($son)); $gp= new $grandparent; $gp->grandParentMethod
because I need to call my grandparent method in the context of $son.
-
a20 over 8 yearsHey Enrique, looks like Krinkle's answer is more suitable and clearer. It also doesn't require you to set public access on grandpa class in order to access it from grandchild class ... if you agree, could you change accepted answer to Krinkle's answer?
-
-
Enrique over 11 yearsI guess we can use setAccessible for non public methods. Anyway, where is $this here? in most cases I need to use grandparent inside my son class, and granparent must change things on my $this (son) object not a new one. The weird thing is that in this case PHP (5.3.13) is not showing the E_SRICT warning when I do something like: GrandParent::method(); inside my Son class (and also it's assuming the correct $this).
-
Dr.Molle over 11 yearsI can't tell you why the call of
GrandParent::method()
from within the class does not force the STRICT-warning. $this will be the object that invokes the method, $son in this case, so changes to $this will apply to $son here. -
Marcelo Diniz about 11 yearsI had the same question, but according to this official bug report, if the context is right (I assume that means 'from a subclass'), the call
GrandParent::method()
will set$this
correctly and allow this non-static call in a static way. -
Brian over 8 yearsThis is a much cleaner approach than the accepted answer. I didn't even know you could do this in PHP until today... Thanks @Krinkle
-
Tofandel almost 3 yearsThis doesn't work in static context, eg calling Base::getFoo() which is static will not bind the static context