Как получить конструктор класса PHP для вызова конструктора родительского родительского элемента - PullRequest
186 голосов
/ 13 октября 2009

Мне нужно, чтобы конструктор класса в PHP вызывал конструктор его родителя parent (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
    }
}

Я знаю, что это странная вещь, и я пытаюсь найти средство, которое не пахнет плохо, но, тем не менее, мне любопытно, возможно ли это.

EDIT

Я подумал, что должен опубликовать обоснование для выбранного ответа. Причина в том, это наиболее элегантное решение проблемы вызова конструктора «дедушки и бабушки» при сохранении всех значений. Это, конечно, не лучший подход и не ООП-дружественный, но вопрос не в этом.

Для тех, кто сталкивается с этим вопросом позднее - Пожалуйста, найдите другое решение . Я смог найти гораздо лучший подход, который не нанес бы ущерба структуре класса. Так и вам.

Ответы [ 15 ]

137 голосов
/ 13 октября 2009

Уродливый обходной путь - передать булеву параметру Papa, указывающий, что вы не хотите анализировать код, содержащийся в его конструкторе. то есть:

// 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);
    }
}
69 голосов
/ 13 октября 2009

Вы должны использовать Grandpa::__construct(), другого ярлыка для него нет. Кроме того, это разрушает инкапсуляцию класса Papa - при чтении или работе с Papa можно с уверенностью предположить, что метод __construct() будет вызываться во время построения, но класс Kiddo этого не делает .

50 голосов
/ 18 января 2012
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();
    }
}
19 голосов
/ 13 октября 2009

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

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

Я проголосовал за два других ответа, которыепредоставил правильные, но уродливые решения для уродливого вопроса:)

16 голосов
/ 22 марта 2015

Красивое решение с использованием 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();
16 голосов
/ 13 октября 2009

Еще один вариант, который не использует флаг и может работать в вашей ситуации:

<?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";
9 голосов
/ 18 сентября 2013

Я согласен с «слишком много 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

1010 * Дедушка *

Это функция не ошибка, проверьте это для справки:

https://bugs.php.net/bug.php?id=42016

Так оно и есть. Если он видит, что исходит из правильного контекста, эта версия вызова не обеспечивает статический вызов.

Вместо этого он просто сохранит $ this и будет счастлив с этим.

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

Дедушка

Hello

Но если вы попытаетесь инициализировать новый Papa, вы получите ошибку E_STRICT:

$papa = new Papa;

Строгие стандарты: нестатический метод Kiddo :: hello () не должен вызываться статически, предполагая $ this из несовместимого контекста

Вы можете использовать instanceof, чтобы определить, можно ли вызвать метод Children :: method () в родительском методе:

if ($this instanceof Kiddo) Kiddo::hello();
8 голосов
/ 14 июня 2010

Для этого есть более простое решение, но оно требует, чтобы вы точно знали, сколько наследования прошел ваш текущий класс. К счастью, аргументы 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 (), но в контролируемых обстоятельствах оно работает как задумано.

7 голосов
/ 17 сентября 2012

Вы можете вызывать конструкцию 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();
4 голосов
/ 04 марта 2016

Забавные подробности о 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) (не частной) функции.

Внешний класс def

$test::paFkt();

выдаст строгую ошибку (нестатическая функция).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...