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

Странное поведение при переопределении частных методов

Рассмотрим следующий фрагмент кода:

class foo {
    private function m() {
        echo 'foo->m() ';
    }
    public function call() {
        $this->m();
    }
}

class bar extends foo {
    private function m() {
        echo 'bar->m() ';
    }
    public function callbar() {
        $this->m();
    }
}

$bar = new bar;

$bar->call();
$bar->callbar();

Теперь, изменяя видимость метода m(), я получаю:
(+ для public, - для private)

Visibility              bar->call()    bar->callbar() 
======================================================
-foo->m(), -bar->m()    foo->m()       bar->m()
-foo->m(), +bar->m()    foo->m()       bar->m()
+foo->m(), -bar->m()    ERROR          ERROR
+foo->m(), +bar->m()    bar->m()       bar->m()

(protected, похоже, ведет себя как public).

Я ожидал, что все будет вести себя так, как если бы они были объявлены public. Но хотя foo->call() и bar->callbar() суть одно и то же, они дают разные результаты в зависимости от видимости m() в foo и bar. Почему это происходит?

4b9b3361

Ответ 1

Наследование/переопределение частных методов

В PHP методы (включая частные) в подклассах:

  • скопирован; объем исходной функции поддерживается.
  • Заменено ( "переопределено", если вы хотите).

Вы можете увидеть это с помощью этого кода:

<?php
class A {
    //calling B::h, because static:: resolves to B::
    function callH() { static::h(); }
    private function h() { echo "in A::h"; }
}
class B extends A {
    //not necessary; just to make explicit what happening
    function callH() { parent::callH(); }
}
$b = new B;
$b->callH();

Теперь, если вы переопределите частный метод, его новая область не будет A, это будет B, и вызов завершится неудачно, потому что A::callH() работает в области A:

<?php
class A {
    //calling B::h, because static:: resolves to B::
    function callH() { static::h(); }
    private function h() { echo "in A::h"; }
}
class B extends A {
    private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'

Методы вызова

Здесь следующие правила:

  • Посмотрите в таблицу методов фактического класса объекта (в вашем случае bar).
    • Если это дает закрытый метод:
      • Если область, в которой был определен метод, совпадает с областью действия вызывающей функции и совпадает с классом объекта, используйте его.
      • В противном случае просмотрите родительские классы для частного метода с той же областью действия, что и вызывающая функция и с тем же именем.
      • Если метод не найден, который удовлетворяет одному из вышеперечисленных требований, выполните сбой.
    • Если это дает общедоступный/защищенный метод:
      • Если область действия метода отмечена как измененная, мы можем переопределить частный метод с помощью общедоступного/защищенного метода. Таким образом, в этом случае, и, кроме того, там используется метод с тем же именем, который является private, как определено для области вызывающей функции.
      • В противном случае используйте найденный метод.

Заключение

  • (оба частные) Для bar->call() область call равна foo. Вызов $this->m() вызывает поиск в таблице методов bar для m, что дает частный bar::m(). Однако область bar::m() отличается от области вызова, которая foo. Метод foo:m() найден при прохождении иерархии и используется вместо этого.
  • (Закрыто в foo, public в bar) Объем call по-прежнему foo. Результатом поиска является публикация bar::m(). Однако его область помечена как измененная, поэтому поиск выполняется в таблице функций области вызова foo для метода m(). Это дает частный метод foo:m() с той же областью действия, что и область вызова, поэтому используется вместо этого.
  • Здесь ничего не видно, потому что видимость снижена.
  • (Оба общедоступных) Объем call по-прежнему foo. Результатом поиска является публикация bar::m(). Его область не помечена как измененная (они оба являются общедоступными), поэтому используется bar::m().

Ответ 2

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

Если у вас есть метод, который вы хотите использовать из родительского класса, но хотите, чтобы дети могли изменять его поведение и не хотите, чтобы этот метод был доступен извне, используйте protected. Если вы хотите, чтобы функциональность вашего родительского класса никак не могла быть изменена подклассами, определите метод как private.

EDIT:, чтобы уточнить, если у вас есть два метода с тем же именем в родительском и подклассе, и эти методы определены как закрытые, по существу метод подкласса абсолютно не имеет отношения к родительскому метод. Как указано, частный метод ПОЛНОСТЬЮ НЕВИДИМО для подкласса.

Рассмотрим это:

class foo {
    private function m() {
        echo 'foo->m() ';
    }
    private function z() { echo "foo->z();"; }

    public function call() {
        $this->m();
    }
}

class bar extends foo {
    private function m() {
        echo 'bar->m() ';
    }
    public function callbar() {
        $this->m();
    }
    public function callz()
    {
       $this->z();
    }
}

Вызов $bar->callz(); собирается создать ERROR, поскольку z вообще не существует в подклассе, даже не как унаследованный метод.

Ответ 3

Согласно руководству PHP:

Члены, объявленные как частные, могут доступ к классу, который определяет член.

http://www.php.net/manual/en/language.oop5.visibility.php

ИЗМЕНИТЬ

они дают разные результаты в зависимости на видимость m() в foo и бар. Почему это происходит?

Если m() в foo является общедоступным, оно является допустимым. Если в этом случае m() из bar переопределяет m() в foo.