Поведение $ this на унаследованных методах - PullRequest
2 голосов
/ 24 февраля 2012

Мне всегда казалось, что я понимаю, как работает ООП (и я использую его годами), но иногда я понимаю, что некоторые концепции до сих пор мне не так понятны.

Я только что натолкнулся на этот вопрос о видимости метода в PHP. В принятом ответе объясняется, что закрытый метод не может быть переопределен дочерним классом в PHP. Хорошо, это имеет смысл. Однако этот пример заставил меня задуматься о механизме внутреннего наследования в PHP и о том, как $this ведет себя на унаследованных методах.

Рассмотрим этот код ( пример из руководства по PHP , также включенного в вопрос, упомянутый выше):

class Bar 
{
    public function test() {
        $this->testPrivate();
        $this->testPublic();
    }

    public function testPublic() {
        echo "Bar::testPublic\n";
    }

    private function testPrivate() {
        echo "Bar::testPrivate\n";
    }
}

class Foo extends Bar 
{
    public function testPublic() {
        echo "Foo::testPublic\n";
    }

    private function testPrivate() {
        echo "Foo::testPrivate\n";
    }
}

$myFoo = new foo();
$myFoo->test();

/* 
Output:

Bar::testPrivate
Foo::testPublic
*/

Теперь рассмотрим этот отрывок из Руководства по PHP :

Псевдопеременная $ this доступна, когда метод вызывается из контекста объекта. $ это ссылка на вызывающий объект (обычно это объект, к которому принадлежит метод, но, возможно, другой объект, если метод вызывается статически из контекста вторичного объекта).

Объяснение гласит, что «$this является ссылкой на вызывающий объект», то есть $myFoo. Поэтому я ожидал, что $myFoo->test() всегда будет вызывать Foo::testPrivate и никогда Bar::testPrivate (если $myFoo не является экземпляром Bar). Я тестировал $this с get_class, и он всегда возвращает Foo, даже изнутри Bar::testPrivate и Bar::test. Однако $this ведет себя как экземпляр Bar, когда Bar::test вызывает $this->testPrivate().

Это действительно сбивает с толку, и я пытаюсь понять , почему так работает!

Я думал, что унаследованные методы (public или protected) были каким-то образом скопированы из базового в дочерний класс. Частные методы не будут скопированы вообще. Но этот пример показывает, что это не работает так. Похоже, что экземпляр Foo хранит внутренний экземпляр Bar и делегирует вызовы методов при необходимости.

Я пытаюсь чему-то научиться здесь, и я учусь только тогда, когда вещи имеют для меня смысл. Этот не делает. После написания всего этого, я думаю, я могу обобщить это двумя вопросами:

  1. Может ли кто-нибудь кратко объяснить, как работает наследование внутри в PHP? Или, по крайней мере, указать мне статью или документацию об этом?

  2. Поведение или $this, обсуждаемое здесь, присутствует и на других языках OO, или оно специфично для PHP?

Ответы [ 2 ]

5 голосов
/ 24 февраля 2012

Наследование в PHP работает так же, как и в большинстве объектно-ориентированных языков.

Когда у вас есть «виртуальный» метод, метод не привязан напрямую к вызывающей стороне. Вместо этого каждый класс содержит небольшую таблицу поиска, в которой говорится, что «имя этого метода связано с этой реализацией». Итак, когда вы говорите $this->testPublic(), на самом деле происходит следующее: PHP:

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

Поскольку Foo переопределяет testPublic, его виртуальная таблица содержит запись для testPublic, указывающую на Foo::testPublic.

Теперь с закрытыми методами поведение другое. Поскольку, как вы правильно прочитали, закрытые методы не могут быть переопределены, вызов частного метода никогда не приводит к поиску виртуальной таблицы . То есть закрытые методы не могут быть виртуальными и всегда должны быть определены в классе, который их использует.

Таким образом, в результате объявления имя связывается во время объявления: все Foo методы будут вызывать Foo::testPrivate, когда они говорят $this->testPrivate, а все Bar методы будут вызывать Bar::testPrivate. * 1030. *

Подводя итог, сказать, что "унаследованные методы копируются в дочерний элемент" не правильно. На самом деле происходит то, что дочерний процесс начинается с того, что его таблица-имя-метода-метода заполняется записями родительского класса, а затем добавляет свои собственные функции и заменяет любые переопределенные записи. Когда вы вызываете $this->something, эта справочная таблица обращается к для текущего класса объекта . Так что если $this является экземпляром Foo, а Foo переопределяет testPublic, вы получите Foo::testPublic. Если $this является экземпляром Bar, вы получите Bar::testPublic.

2 голосов
/ 24 февраля 2012

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

Таким образом, при использовании $this в сочетании с методом или свойством private это всегда будет метод или свойство в том же классе, что и ссылка на $this. Это происходит потому, что $this, вызываемый в родительском классе, не может получить доступ к private методам или свойствам в другом классе (потому что они являются частными), даже из дочерних классов .

Надеюсь, это поможет.

...