Как вызвать метод grandparent без получения ошибки E_STRICT?
Иногда мне нужно выполнить метод grandparent (то есть обходить родительский метод), я знаю, что это запах кода, но иногда я не могу изменить другие классы (фреймворки, библиотеки и т.д.).
В PHP мы можем сделать что-то вроде:
call_user_func(array(get_parent_class(get_parent_class($childObject)), 'grandParentMethod'));
Проблема в том, что если у вас есть ошибки E_STRICT, вы получите сообщение об ошибке:
Строгие стандарты: нестатический метод GrandParent:: grandParentMethod() не следует называть статически...
Я нашел только одно решение для этого (без удаления E_STRICT), и он просто добавляет @
для подавления ошибки.
Но что действительно уродливо, знает ли кто-нибудь лучшее решение?
Спасибо!
PS: Я не могу создать экземпляр нового объекта, например:
$grandparent = get_parent_class(get_parent_class($son));
$gp= new $grandparent;
$gp->grandParentMethod
потому что мне нужно вызвать мой метод бабушки и дедушки в контексте $son.
Ответы
Ответ 1
Вы можете использовать ReflectionMethod- > invoke()
Пример:
<?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
Примечание. Вызываемый метод должен быть общедоступным.
Ответ 2
Вы можете вызвать прародителя напрямую по имени (не нужно ни Reflection, ни 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';
}
}
Вызов Base::getFoo
может выглядеть как статический вызов (из-за синтаксиса двоеточия ::
:), но это не так. Также как parent::
не является статичным.
Вызов метода из цепочки наследования внутри класса правильно связывает значение $this
, вызывает его как обычный метод, соблюдает правила видимости (например, защищен) и не является нарушением любого вида!
Поначалу это может показаться немного странным, но это способ сделать это в PHP.
Ответ 3
Вы можете использовать отдельный внутренний метод (например, _doStuff
для дополнения doStuff
) и вызвать его непосредственно от внука через родителя.
// Base class that everything inherits
class Grandpa {
protected function _doStuff() {
// grandpa 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();
показывает
grandpa
grandpa papa
grandpa kiddo
Ответ 4
Тимо ответ работает, но я думаю, что он работает случайно больше, чем дизайн. Если вы посмотрите на коды операций для $this-> doX против parent :: doX против Grandparent :: doX, вы увидите, что Grandparent :: doX вызывается как статический метод, но PHP будет использовать $ this, который находится в области видимости
$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
Из-за этого параметр $ this доступен в статическом вызове дедушки (из https://www.php.net/manual/en/language.oop5.basic.php):
Псевдопеременная $ this доступна, когда метод вызывается из контекста объекта. $ это ссылка на вызывающий объект (обычно это объект, к которому принадлежит метод, но, возможно, другой объект, если метод вызывается статически из контекста вторичного объекта). Начиная с PHP 7.0.0, статический вызов нестатического метода из несовместимого контекста приводит к тому, что $ this не определено внутри метода. Вызов нестатического метода статически из несовместимого контекста устарел с версии PHP 5.6.0. Начиная с PHP 7.0.0, статический вызов статического метода обычно не рекомендуется (даже если он вызывается из совместимого контекста). До PHP 5.6.0 такие вызовы уже вызывали строгое уведомление.