Почему дочерний элемент может переопределить и получить доступ к своему родительскому частному методу?

Я привык помещать метод как protected в свои классы PHP. Но, играя с private, я начинаю сомневаться.

Я знаю, что это может быть дубликат, но я не могу найти ясности в SO-ответах, которые я прочитал. Проверены документация и SO ответы, но все же мои тесты не соответствуют тому, что там говорится (см. ниже) .

В частности, не часто объясняется своего рода «запутывание» между методами public и private при расширении класса одного и того же типа в контексте PHP.

Например:

<?php

class A
{
    private function ab() { echo 'test'.PHP_EOL; }
    public function test() { $this->ab(); }
    public function accessprivate($obj)  { $obj->ab(); }
}

class B extends A
{
        public function ab() { echo 'overridden-public'.PHP_EOL; }  // expect notice overriding private parent method
}

$a = new A;
$a2 = new A;
$b = new B;

$a->test(); // expect 'test'
$b->test(); // expect access to B::ab() and print 'overridden-public'
$b->ab();   // expect access to B::ab() and print 'overridden-public'
$a2->accessprivate($a); // expect 'test' since is the same class
$b->accessprivate($a); // expect cannotaccess private of A from class B

При запуске вот такой результат:

test test overridden-public test test

Суть в том, что я ожидал, что метод private унаследован, но недоступен для дочерних классов; следовательно:

  • Я не смогу изменить видимость ab() на public
  • с переопределением ab() в B я ожидал бы, что test() вызовет ab() на $this как экземпляр B и напечатает "overridden-public"
  • accessprivate() с $a2 по $a нормально, потому что они одного класса
  • accessprivate() с $b по $a НЕ должно быть хорошо, потому что это разные классы (родительский и дочерний)

Итак, вопросы:

  • Почему я ошибаюсь? Что я неправильно понимаю?
  • Является ли эта модель видимости такой же на других языках или PHP делает это по-другому? И в таком случае соответствуют ли мои ожидания модели видимости какого-то другого языка?

person Kamafeather    schedule 02.08.2019    source источник
comment
Вы показываете ожидаемые результаты в своем вопросе, но не фактические результаты. Их добавление может быть полезным.   -  person Dave    schedule 02.08.2019
comment
Добавлен. Спасибо за предложение ????   -  person Kamafeather    schedule 02.08.2019
comment
Хороший вопрос. Это действительно причудливо. Примечание: если вы создадите класс C и определите для него public function accessprivate($obj) { $obj->ab(); }, вы получите ожидаемую ошибку. Я предполагаю, что контекст php по-прежнему привязан к A, когда вы делаете $obj->ab(), но я понятия не имею, почему.   -  person k0pernikus    schedule 02.08.2019
comment
Другое примечание: ваш public function ab() на B ничего не отменяет. Он создает совершенно новый метод на B. (Для других языков требуется ключевое слово override. В Php его нет, поэтому это может сбивать с толку.) Таким образом, наличие A::ab() private запрещает родительский вызов в B, поэтому вы не можете выполнять parent::ab() в B из-за частного доступа.   -  person k0pernikus    schedule 02.08.2019


Ответы (1)


Я не могу изменить видимость ab() на public

Переопределяющая реализация должна иметь такую ​​же или более высокую видимость, чтобы не нарушать LSP. . Переопределяющая реализация не раскрывает родительскую реализацию, поэтому здесь нет фундаментальных проблем. Если реализация родителя не общедоступна, во всех смыслах она не существует. Во всех смыслах и целях дочерний элемент добавляет в класс новый метод, и это нормально.

с переопределением ab() в B я бы ожидал, что test() вызовет ab() на $this как экземпляр B и напечатает "overridden-public"

private методы "жестко привязаны" к объявляющему классу. A::test предпочтительно будет вызывать private A::ab. Это сделано специально для того, чтобы внутренние элементы класса могли оставаться private. Если расширяющий класс реализует идентичный метод неосознанно, в A нет ничего удивительного.

accessprivate() с $b по $a НЕ должно быть хорошо, потому что это разные классы (родительский и дочерний)

Он по-прежнему вызывает A::accessprivate, так как B не реализует такой метод, который скорее работает так же, как метод test и объяснение в предыдущем абзаце.

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

person deceze♦    schedule 02.08.2019
comment
Мммм, все еще обрабатываю это; но это начинает иметь смысл, спасибо. Тем не менее, это сложно, поскольку это выглядит как неявное поведение, в то время как я предпочел бы получить уведомление, если я делаю что-то, что мне не разрешено делать (или это не будет иметь никакого эффекта, например, неработающее переопределение моих примеров). По этому поводу документация по PHP не выглядит достаточно ясной. Это поведение явно задокументировано / упомянуто где-либо на php.net? - person Kamafeather; 02.08.2019