Переопределить метод родителя для родительского контекста в PHP

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

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

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

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

диаграмма

Проблема бесконечного цикла

диаграмма

Related of "Переопределить метод родителя для родительского контекста в PHP"

Этот метод использует статические методы

 <?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 

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

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

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

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

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

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

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

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

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

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

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

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

 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" } } 

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

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

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

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

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

Затем, чтобы получить доступ к родительской функции от дочернего InvokeDraw , установите псевдоним 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 */ 

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

 <?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