ООП в 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" или нет.
 
вообще действительно странное поведение. не то, чтобы это нужно было срочно фиксить, но я подивился.

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

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

-~{}~ 24.10.07 12:45:

...кстати, если подумать, то это в некотором смысле замена c++-ного friend :) так что лучше не фиксить, а задокументировать в явном виде, ятд.
 
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() отрабатывает не метод родительского класса?". Типа "область видимости" - методы класса-родителя (ужас, ужас, но допустимо :-)
родительском классе метода вызывает

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

так что предложение считать это friends-like фичей и как-то описать - прикольное :-)
 
Gorynych
скорее всего, замедлит работу классов
неужели поиск имени класса в списке, содержащем иерархию наследования - такая дорогая операция (не знаю как организовано в ZE, просто предположение)
 
Определение из википедии:

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

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

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

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


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

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

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

К примеру, у нас есть некая сторонняя библиотека объявляющая класс 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);
// > Я убит!

В этом примере поведение, которое реализует класс Экстрасенс — неестественно, равно как и нынешнее поведенеи ПХП.
 
Сверху