Мне нужно, чтобы конструктор класса в PHP вызывал конструктор родительского родителя (grandparent?) Без вызова родительского конструктора.
// main class that everything inherits class Grandpa { public function __construct() { } } class Papa extends Grandpa { public function __construct() { // call Grandpa's constructor parent::__construct(); } } class Kiddo extends Papa { public function __construct() { // THIS IS WHERE I NEED TO CALL GRANDPA'S // CONSTRUCTOR AND NOT PAPA'S } }
Я знаю, что это странная вещь, и я пытаюсь найти средства, которые не пахнут плохо, но тем не менее, мне любопытно, если это возможно.
РЕДАКТИРОВАТЬ
Я думал, что должен опубликовать обоснование выбранного ответа. Причина; это наиболее изящное решение проблемы желания вызвать конструктор «grandparent», сохраняя при этом все значения. Это, конечно, не самый лучший подход, и он не подходит для ООП, но это не то, о чем спрашивал вопрос.
Для тех, кто сталкивается с этим вопросом на более поздний срок – см. Другое решение . Мне удалось найти гораздо лучший подход, который не навредил классовой структуре. Так и должно быть.
Уродливым обходным путем было бы передать булевский параметр папе, указывая на то, что вы не хотите анализировать код, содержащийся в его конструкторе. то есть:
// main class that everything inherits class Grandpa { public function __construct() { } } class Papa extends Grandpa { public function __construct($bypass = false) { // only perform actions inside if not bypassing if (!$bypass) { } // call Grandpa's constructor parent::__construct(); } } class Kiddo extends Papa { public function __construct() { $bypassPapa = true; parent::__construct($bypassPapa); } }
Вы должны использовать Grandpa::__construct()
, для этого нет другого ярлыка. Кроме того, это разрушает инкапсуляцию класса Papa
– при чтении или работе над Papa
следует с уверенностью предположить, что во время построения будет вызываться метод __construct()
, но класс Kiddo
этого не делает.
class Grandpa { public function __construct() {} } class Papa extends Grandpa { public function __construct() { //call Grandpa's constructor parent::__construct(); } } class Kiddo extends Papa { public function __construct() { //this is not a bug, it works that way in php Grandpa::__construct(); } }
Другой вариант, который не использует флаг и может работать в вашей ситуации:
<?php // main class that everything inherits class Grandpa { public function __construct(){ $this->GrandpaSetup(); } public function GrandpaSetup(){ $this->prop1 = 'foo'; $this->prop2 = 'bar'; } } class Papa extends Grandpa { public function __construct() { // call Grandpa's constructor parent::__construct(); $this->prop1 = 'foobar'; } } class Kiddo extends Papa { public function __construct() { $this->GrandpaSetup(); } } $kid = new Kiddo(); echo "{$kid->prop1}\n{$kid->prop2}\n";
В итоге я придумал альтернативное решение, которое решило проблему.
Я поддержал два других ответа, которые предоставили правильные, но уродливые решения для более уродливого вопроса 🙂
Красивое решение с использованием Reflection
.
<?php class Grandpa { public function __construct() { echo "Grandpa's constructor called\n"; } } class Papa extends Grandpa { public function __construct() { echo "Papa's constructor called\n"; // call Grandpa's constructor parent::__construct(); } } class Kiddo extends Papa { public function __construct() { echo "Kiddo's constructor called\n"; $reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($this)), '__construct'); $reflectionMethod->invoke($this); } } $kiddo = new Kiddo(); $papa = new Papa();
Для этого есть более простое решение, но для этого требуется, чтобы вы точно знали, какое количество наследования у вашего текущего класса прошло. К счастью, аргументы get_parent_class () позволяют вашему члену массива классов быть именем класса в виде строки, а также самого экземпляра.
Имейте в виду, что это также по своей сути полагается на вызов метода класса __construct () статически, хотя в пределах инкрементной области наследуемого объекта разница в этом конкретном случае пренебрежимо мала (ах, PHP).
Рассмотрим следующее:
class Foo { var $f = 'bad (Foo)'; function __construct() { $this->f = 'Good!'; } } class Bar extends Foo { var $f = 'bad (Bar)'; } class FooBar extends Bar { var $f = 'bad (FooBar)'; function __construct() { # FooBar constructor logic here call_user_func(array(get_parent_class(get_parent_class($this)), '__construct')); } } $foo = new FooBar(); echo $foo->f; #=> 'Good!'
Опять же, это не является жизнеспособным решением для ситуации, когда вы не представляете, сколько наследований произошло из-за ограничений debug_backtrace (), но в контролируемых обстоятельствах оно работает по назначению.
Я согласен с «слишком большим количеством php», попробуйте следующее:
class Grandpa { public function __construct() { echo 'Grandpa<br/>'; } } class Papa extends Grandpa { public function __construct() { echo 'Papa<br/>'; parent::__construct(); } } class Kiddo extends Papa { public function __construct() { // THIS IS WHERE I NEED TO CALL GRANDPA'S // CONSTRUCTOR AND NOT PAPA'S echo 'Kiddo<br/>'; Grandpa::__construct(); } } $instance = new Kiddo;
Я получил результат, как ожидалось:
Kiddo
Дедушка
Это не ошибка, проверьте это для справки:
https://bugs.php.net/bug.php?id=42016
Это так, как это работает. Если он видит, что он исходит из правильного контекста, эта версия вызова не обеспечивает статический вызов.
Вместо этого он просто будет держать это и быть счастливым.
parent :: method () работает одинаково, вам не нужно определять метод как статический, но его можно вызвать в том же контексте. Попробуйте это для более интересного:
class Grandpa { public function __construct() { echo 'Grandpa<br/>'; Kiddo::hello(); } } class Papa extends Grandpa { public function __construct() { echo 'Papa<br/>'; parent::__construct(); } } class Kiddo extends Papa { public function __construct() { // THIS IS WHERE I NEED TO CALL GRANDPA'S // CONSTRUCTOR AND NOT PAPA'S echo 'Kiddo<br/>'; Grandpa::__construct(); } public function hello() { echo 'Hello<br/>'; } } $instance = new Kiddo;
Он также работает так, как ожидалось:
Kiddo
Дедушка
Здравствуйте
Но если вы попытаетесь инициализировать новый папа, вы получите ошибку E_STRICT:
$papa = new Papa;
Строгие стандарты: нестатический метод Kiddo :: hello () не следует называть статически, предполагая $ this из несовместимого контекста
Вы можете использовать instanceof, чтобы определить, можете ли вы вызвать метод Children :: method () в родительском методе:
if ($this instanceof Kiddo) Kiddo::hello();
Вы можете вызвать конструкцию Grandpa :: __, где хотите, и $ this ключевое слово будет ссылаться на ваш текущий экземпляр класса. Но будьте осторожны с этим методом, вы не можете получить доступ к защищенным свойствам и методам текущего экземпляра из этого другого контекста, только к публичным элементам. => Все работы и официальная поддержка .
пример
// main class that everything inherits class Grandpa { public function __construct() { echo $this->one; // will print 1 echo $this->two; // error cannot access protected property } } class Papa extends Grandpa { public function __construct() { // call Grandpa's constructor parent::__construct(); } } class Kiddo extends Papa { public $one = 1; protected $two = 2; public function __construct() { Grandpa::__construct(); } } new Kiddo();
Хорошо, Еще одно уродливое решение:
Создайте функцию в папке:
protected function call2Granpa() { return parent::__construct(); }
Затем в Киддо вы используете:
parent::call2Granpa();
// вместо вызова конструктора в папке.
Я думаю, что это может сработать … Я не тестировал его, поэтому я не уверен, что объекты созданы правильно.
Я использовал этот подход, но с неконструкторскими функциями.
<?php class grand_pa { public function __construct() { echo "Hey I am Grand Pa <br>"; } } class pa_pa extends grand_pa { // no need for construct here unless you want to do something specifically within this class as init stuff // the construct for this class will be inherited from the parent. } class kiddo extends pa_pa { public function __construct() { parent::__construct(); echo "Hey I am a child <br>"; } } new kiddo(); ?>
Конечно, это предполагает, что вам не нужно ничего делать в конструкции pa_pa. Выполнение этого будет выводиться:
Эй, я большой Па Эй, я ребенок
Забавные подробности о php: расширенные классы могут использовать нестатические функции родительского класса в статическом материале. Снаружи вы получите строгую ошибку.
error_reporting(E_ALL); class GrandPa { public function __construct() { print("construct grandpa<br/>"); $this->grandPaFkt(); } protected function grandPaFkt(){ print(">>do Grandpa<br/>"); } } class Pa extends GrandPa { public function __construct() { parent::__construct(); print("construct Pa <br/>"); } public function paFkt(){ print(">>do Pa <br>"); } } class Child extends Pa { public function __construct() { GrandPa::__construct(); Pa::paFkt();//allright //parent::__construct();//whatever you want print("construct Child<br/>"); } } $test=new Child(); $test::paFkt();//strict error
Поэтому внутри расширенного класса (Child) вы можете использовать
parent::paFkt();
или
Pa::paFkt();
для доступа к родительской (или grandPa's) (не частной) функции.
Вне класса def
$test::paFkt();
будет вызывать строгую ошибку (не статическая функция).
// main class that everything inherits class Grandpa { public function __construct() { $this->___construct(); } protected function ___construct() { // grandpa's logic } } class Papa extends Grandpa { public function __construct() { // call Grandpa's constructor parent::__construct(); } } class Kiddo extends Papa { public function __construct() { parent::___construct(); } }
обратите внимание, что «___construct» – это не какое-то волшебное имя, вы можете назвать его «doGrandpaStuff».
class Grandpa { public function __construct() { echo"Hello Kiddo"; } } class Papa extends Grandpa { public function __construct() { } public function CallGranddad() { parent::__construct(); } } class Kiddo extends Papa { public function __construct() { } public function needSomethingFromGrandDad { parent::CallGranddad(); } }
от php 7 u можете использовать
parent::parent::__construct();