Как получить конструктор класса PHP для вызова его родительского конструктора

Мне нужно, чтобы конструктор класса в 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"; 

В итоге я придумал альтернативное решение, которое решило проблему.

  • Я создал промежуточный класс, который расширил дедушку.
  • Тогда и Папа, и Киддо расширили этот класс.
  • Киддо требовал некоторой промежуточной функциональности Papa, но не хотел, чтобы он был конструктором, поэтому класс обладает этой дополнительной функциональностью и расширяет ее.

Я поддержал два других ответа, которые предоставили правильные, но уродливые решения для более уродливого вопроса 🙂

Красивое решение с использованием 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();