Подтвердить что ты не робот

Доступ к родительскому переопределенному методу из родительского контекста в PHP

У меня есть класс PHP с рисунком ClassA, который расширяется многими другими классами рисования, например ClassB.

Мне нужно, чтобы унаследованные классы запускали родительские классы Draw(). Однако в моей конкретной ситуации я не хочу напрямую обращаться к такому методу (например: parent::Draw()). Мне нужна третья функция (например, parent::InvokeDraw()), чтобы вызвать мой метод рисования из родительского контекста.

Вот какой код для иллюстрации:

class ClassA
{
    function Draw()
    {

        /* Drawing code ... */

    }

    function InvokeDraw()
    {
        $this->Draw();
    }
}

class ClassB extends ClassA
{
    function Draw()
    {
        parent::InvokeDraw();

        /* Drawing code ... */

    }
}

Проблема, с которой я сталкиваюсь, заключается в том, что InvokeDraw() не будет вызывать родительский метод Draw(), а расширенный класс Draw (расширенный класс) ), что вызывает бесконечный цикл.

Несмотря на то, что проблема довольно логична, мне сложно найти решение этой проблемы. Как выполнить эту задачу?

Желаемый эффект

chart

Бесконечная задача цикла

chart

4b9b3361

Ответ 1

Это с использованием статических методов

<?php

class ClassA
{
    function Draw()
    {
        echo "DrawA";
        /* Drawing code ... */

    }

    function InvokeDraw()
    {
        self::Draw();
    }
}

class ClassB extends ClassA
{
    function Draw()
    {
        echo "DrawB";
        parent::InvokeDraw();

        /* Drawing code ... */

    }
}

echo "Begin:<br>";

$cb = new ClassB();
$cb->Draw();

Обратите внимание, что единственное, что я изменил, это метод InvokeDraw() и использовал его self, который относится к классу, а не к объекту, как к $this

Вывод:

Begin:
DrawBDrawA

Edit: Чтобы ответить на ваш комментарий ниже, я добавлю краткое описание того, как работает ваш код и как работает этот код.

Что происходит в вашем коде:

  • Мы создаем B и начинаем работать с ним
  • Мы вызываем B->Draw() при работе в объекте B class AND B.
  • B->Draw() вызывает статически (это означает метод класса) A::Invoke() из класса A НО, мы все еще используем объект B.
  • Статический вызов A::Invoke() вызывает $this->Draw();, и поскольку мы сейчас работаем с объектом B, $this ссылается на экземпляр ClassB.
  • И здесь мы начинаем цикл.

Что происходит в коде выше:

  • Мы создаем B и начинаем работать с ним
  • Мы вызываем B->Draw() при работе в объекте B class AND B.
  • B->Draw() вызывает статически (это метод класса) A::Invoke из класса A BUT, а также в вашем коде мы все еще используем B объект.
  • Статический вызов A::Invoke() вызывает self::Draw(), который в основном такой же, как ClassA::Draw(), и потому что это статический метод, нам не важно, с каким объектом мы в настоящее время работаем, и вызываем A Draw() метод.
  • A::Draw() метод выполняется по мере необходимости.

Я приведу то же объяснение для кода в моем втором ответе, который не использует статические вызовы:

  • Мы создаем B и начинаем работать с ним
  • Мы вызываем B->Draw() при работе в объекте B class AND B.
  • B->Draw() СОЗДАЕТ экземпляр A.
  • B->Draw() вызывает A->Invoke(), что означает, что мы начинаем работать с объектом, являющимся экземпляром класса A, а не B, как раньше.

В этот момент мы полностью забываем, что B даже существует и работает только с A

  • A->Invoke() вызывает $this->Draw(), что означает, что мы вызываем A->Draw(), потому что мы уже работаем с экземпляром класса A.
  • A->Draw() выполняется, как мы ожидаем.

С точки зрения удобства использования мы видим, что статический метод лучше, так как мы можем определить некоторые статические свойства, и вы можете работать с ними, когда выполняется A::Draw(). Если мы используем нестатический метод, нам нужно передать нужные данные в аргументах наших методов.

Надеюсь, это ясно. Вышеприведенные последовательности не имеют правильной терминологии, но это было написано специально, я думаю, что легче понять поток таким образом.

Ответ 2

На поверхностном уровне наследование работает как слияние кода. Наследуемые методы являются частью дочернего класса, как если бы они были родными для класса:

class A {

    public function foo() {
        echo 'foo';
    }

    public function bar() {
        echo 'bar';
    }

}

class B extends A { }

Для всех целей и целей это эквивалентно написанию:

class B {

    public function foo() {
        echo 'foo';
    }

    public function bar() {
        echo 'bar';
    }

}

Класс B имеет функцию foo и bar, как если бы они были частью его объявления.

Переопределение методов в дочернем классе заменяет одну конкретную реализацию другой:

class B extends A {

    public function bar() {
        echo 'baz';
    }

}

Это как если бы B был объявлен следующим образом:

class B {

    public function foo() {
        echo 'foo';
    }

    public function bar() {
        echo 'baz';
    }

}

За одним исключением: родительская реализация метода доступна с использованием ключевого слова parent. Он не меняет контекста, в котором выполняется код, просто использует родительскую реализацию метода:

class B extends A {

    public function bar() {        public function bar() {
        parent::bar();      --->       echo 'bar';
    }                              }

}

Он работает в том же контексте текущего экземпляра B, он просто вытаскивает старый код родителя из эфира.

Если вы вызываете другой метод в родительском методе, он выполняется в контексте дочернего элемента, а не в контексте родительского класса. Поскольку вы не создали экземпляр родителя, вы работаете с ребенком. Чтобы продемонстрировать со свойствами (он работает одинаково с методами):

class A {

    protected $value = 'bar';

    public function bar() {
        echo $this->value;
    }

}

class B extends A {

    protected $value = 'baz';

    public function bar() {
        parent::bar();     // outputs "baz"
        echo $this->value; // outputs "baz"
    }

}

Как таковая, нет решения вашей проблемы. Вы не можете вызвать метод в "родительском контексте". Вы можете вызвать код родителя в текущем контексте, что все. То, что вы создаете, - это простой цикл, потому что неважно, наследуется ли код или нет, все работает в одном контексте.

Вам нужно перепроектировать этот класс, чтобы не вызывать методы в цикле.

Ответ 3

Как сказал deceze, нет решения вашей проблемы, если вы не перепроектируете класс.

Если вы используете PHP 5.4, вы можете использовать Traits.

Создайте 2 черты, каждая из которых имеет свою собственную функцию Draw. Затем используйте один признак в родительском, а другой - в дочернем.

Затем, чтобы получить доступ к родительской функции из дочернего элемента, установите псевдоним InvokeDraw в родительскую функцию Draw.

Вот пример:

<?php

trait DrawingA
{
    function Draw()
    {
        echo 'I am the parent';
    }
}

trait DrawingB
{
    function Draw()
    {
        echo 'I am the child';
    }
}

class ClassA
{
    use DrawingA;
}

class ClassB extends ClassA
{
    use DrawingA, DrawingB {
        DrawingB::Draw insteadof DrawingA;
        DrawingA::Draw as InvokeDraw;
    }
}

$b = new ClassB();

echo 'Child: ',  $b->Draw(), PHP_EOL;
echo 'Parent: ', $b->InvokeDraw() . PHP_EOL;

/*
Outputs:

Child: I am the child
Parent: I am the parent

*/

Ответ 4

Мне очень понравился ваш вопрос, так что я немного изучил и попытался сделать то же самое, что и в своем первом ответе, но без статических вызовов:

<?php

class ClassA
{

    function Draw()
    {
        echo "DrawA";
        /* Drawing code ... */

    }

    function InvokeDraw()
    {
        $this->Draw();
    }
}

class ClassB extends ClassA
{

    function Draw()
    {
        echo "DrawB";
        $x = new parent;
        $x->InvokeDraw();

        /* Drawing code ... */

    }
}

echo "Begin:<br>";

$cb = new ClassB();
$cb->Draw();

Вывод:

Begin:
DrawBDrawA