ООП в PHP. Вызов защищенных методов в новых экземплярах класса.

XtremallyPurpur

Новичок
ООП в PHP. Вызов защищенных методов в новых экземплярах класса.

Добрый день!

Я завел баг, но его закрыли с пометкой "Bogus".
Все-таки я считаю это багом.
Посмотрите код:

PHP:
class a
{
  protected function aa()
  {
    echo "a-aa\n";
  }
}

class b extends a
{
  protected function aa()
  {
    echo "b-aa\n";
  }  
}
// Если класс "c" наследует от "a" - тогда в новых экземплярах "a" и "b" мы можем вызывать их защищенные методы
// Если класс "c" не наследует от "a" - тогда вызывать защищенные методы нельзя
class c extends a 
{
  public function cc()
  {
    $obj = new a();
    $obj->aa();
    
    $obj2 = new b();
    $obj2->aa();
  }

}

$c = new c();
$c->cc();
Таким образом, если "c" является потомком от "a", из "с" можно вызывать защищенные методы экземпляров любого потомка "a".
По-большому счету, проблема не очень серьезная. Меня интересует, прав я или нет с точки зрения идеологии.

PS. Я воспроизвел этот код на C++. В коде на плюсах поведение как я и ожидал - вызывать подобным образом защищенные методы нельзя, вне зависимости является ли "c" потомком "a" или нет.
 

fixxxer

К.О.
Партнер клуба
вообще действительно странное поведение. не то, чтобы это нужно было срочно фиксить, но я подивился.

дай своё определение "protected method".
я бы определил так - имя защищенного метода определено только относительно $this/self в методах класса, в котоором он непосредственно объявлен, и в методах класса, являющихся потомком данного.

впрочем, поведение php можно считать фичей :)

-~{}~ 24.10.07 12:45:

...кстати, если подумать, то это в некотором смысле замена c++-ного friend :) так что лучше не фиксить, а задокументировать в явном виде, ятд.
 

Gorynych

Посетитель PHP-Клуба
tony2001 - я, конечно, не автор топика, но реально склонен считать такое поведение неожиданным (я, например, такого не ожидал :)

что касается понимания, что есть "protected method", то я искренне полагал, что это методы, доступные внутри класса и внутри его потомком (а не соседей по наследованию).

по-моему в данном случае есть некоторый глючок. Смотри:
PHP:
class a
{
 protected function aa()
 {
   echo "a-aa\n";
 }
}

// класс "b' - потомок "a"
class b extends a
{
 // защищенный метод, перекрывающий аналогичный
 // метод родительского
 protected function aa()
 {
   echo "b-aa\n";
 }

 // защищенный метод, не декларированный в родительском
 // классе!!!
 protected function bb()
 {
   echo "b-bb\n";
 }

}

// класс "с' - такой же потомок "a", как и класс "b"
class c extends a
{
 public function cc()
 {
   $obj = new a();
   $obj->aa();            // мы ведь наследовали класс от "a",
                                 // спорно, но почему бы и нет?

   $obj2 = new b();
   $obj2->aa();         // Проходит вызов защищенного метода, 
                                // декларированного в ОБЩЕМ родительском
                                // классе ! 
                                // И отрабатывает не метод общего родителя,
                                // а метод, который не должен быть доступен.
                                // Сработало перекрытие операций? 

   $obj2->bb();         // а вот вызов защищенного метода, 
                                // не описанного в родительском классе
                                // вызывет Fatal error
 }

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

если вдруг подумать об общем родительском классе, как о пространстве допустимых вызовов, то можно дойти до вопроса: "а почему при вызове $b->aa() отрабатывает не метод родительского класса?". Типа "область видимости" - методы класса-родителя (ужас, ужас, но допустимо :)
родительском классе метода вызывает

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

Gorynych

Посетитель PHP-Клуба
...еще немного подумав: я свечку не держал, но склонен предполагать, что изменение этого поведения выльется в ужас/ ужас сколько возни + скорее всего, замедлит работу классов (запрещается исследование причин по которым программа работает :)

так что предложение считать это friends-like фичей и как-то описать - прикольное :)
 

zerkms

TDD infected
Команда форума
Gorynych
скорее всего, замедлит работу классов
неужели поиск имени класса в списке, содержащем иерархию наследования - такая дорогая операция (не знаю как организовано в ZE, просто предположение)
 

XtremallyPurpur

Новичок
Определение из википедии:

К защищённым методам нельзя обращаться извне класса, чтобы не нарушить целостность данных класса.

Определение из Буча:
protected - методы видимые самому классу, его подклассам и друзьям.

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

Но с другой стороны, экземпляры объектов созданные в c::cc() - это совершенно "посторонние" объекты для объектов типа "с". И вызов их защищенных методов недопустим.
 

Gorynych

Посетитель PHP-Клуба
zerkms - мне кажется, что тут произошло именно перекрытие операций при раскрутке иерархии. Значит где-то не дорабатывает проверка, значит если переделывать, то придется ее докручивать поверх. Я не знаю, как организовано, но предполагаю что обойдется недешево.


XtremallyPurpur если бы вызов $b->aa() выполнял вызов метода родительского класса, это была бы одна фигня. А так...

если отнаследовать еще один класс, уже от "b", вызов его метода "aa" так же будет доступен внутри класса "c".

в такой ситуации проще "записать в друзья" все классы, у которых есть общий предок (пользовательского уровня).
 

DeadMorozBLR

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

К примеру, у нас есть некая сторонняя библиотека объявляющая класс Foo, и в приложении существует объект $foo1 класса Foo, инкапсулирующий (т.е. скрывающий) в себе свое состояние. По логике работы PHP получается, что если в другом месте приложения создать еще один объект $foo2 класса Foo, то он сможет получить доступ непосредственно к состоянию объекта $foo1 минуя интерфейс. В чем тогда смысл областей видимости?

Рассмотрим такое поведение на примере:
PHP:
<?php

class Человек
{
    private $_убит = false;

    public function убейСебяОбСтену()
    {
        $this->_убъюКаЯСебяОбСтену();
    }

    protected function _убъюКаЯСебяОбСтену()
    {
        $this->_убит = true;
        echo 'Я убит';
    }

    public function убить($человек)
    {
        $человек->убейСебяОбСтену();
    }
}

class УмныйЧеловек extends Человек
{
    public function убейСебяОбСтену()
    {
        echo 'Пошел вон, ублюдок!';
    }
}

class Экстрасенс extends Человек
{
    public function убить($человек)
    {
        $человек->_убъюКаЯСебяОбСтену();
    }
}

$человек1 = new Человек();
$человек2 = new УмныйЧеловек();
$человек3 = new Экстрасенс();

$человек1->убить($человек2);
// > Пошел вон, ублюдок!

$человек3->убить($человек2);
// > Я убит!
В этом примере поведение, которое реализует класс Экстрасенс — неестественно, равно как и нынешнее поведенеи ПХП.
 
Сверху