2012-10-05 12 views
12

Znalazłem coś, co wydaje się być dziwnym problemem dziedziczenia w PHP.Dziedziczenie PHP i widoczność chronionych członków

From the PHP manual:

Członkowie oświadczył chronione mogą być dostępne tylko w klasie sama i dziedzicznych i dominujących klas.

Dla mnie to oznacza: A mogą uzyskać dostęp do chronionych członków B, jeśli A instanceof B lub B instanceof A.

Jeśli jednak A i B rozszerzają Foo, a Foo ma chroniony konstruktor, który nie jest nadpisany w B, to mogę utworzyć instancję B z wnętrza A. Nie ma to dla mnie sensu, ponieważ A jest nie instancja B i B nie jest instancją klasy A. Mogę również wywołać metodę chronioną $b->test() z poziomu A, która wykonuje metodę zaimplementowaną w B. (Jeśli B nie będzie redeclare test(), wówczas wykonywana jest implementacja w Foo.) Dla mnie jest to jeszcze bardziej dziwne, ponieważ nie mogę utworzyć instancji B z wnętrza A, jeśli B bezpośrednio implementuje chroniony konstruktor. Wydaje się dziwne, że nie mam dostępu do chronionego konstruktora (również zadeklarowanego w klasie nadrzędnej), ale dostęp do chronionej metody (również zadeklarowanej w klasie nadrzędnej) nie stanowi problemu.

Należy zauważyć, że dostaję oczekiwane zachowanie, gdy używam klasy C, która nie rozszerza Foo. Jeśli próbuję utworzyć instancję B z poziomu C, otrzymuję błąd krytyczny, ponieważ próbuję uzyskać dostęp do chronionego konstruktora. Jeśli dodaję publiczny konstruktor do B, można go utworzyć (co jest oczekiwane) i nadal nie mogę uzyskać dostępu do chronionej metody test() (jest to również oczekiwane zachowanie). Oczekuję tego samego zachowania podczas używania zamiast C

przykładowy kod, który wyjaśnia znowu:

class Foo { 
    protected function __construct() { 
     echo('Constructing ' . get_called_class()); 
    } 

    protected function test() { 
     echo('Hello world ' . __METHOD__); 
    } 
} 

class A extends Foo { 
    public function __construct() { 
     parent::__construct(); 
    } 

    public function testB() { 
     // Both of these lines work 
     $b = new B(); 
     $b->test(); 
    } 
} 

class B extends Foo { 
    protected function test() { 
     echo('Hello world Again ' . __METHOD__); 
    } 
} 

class C { 
    public function __construct() { 
    } 

    public function testB() { 
     // Both of these lines cause fatal errors 
     $b = new B(); 
     $b->test(); 
    } 
} 

$a = new A(); 
$a->testB(); 

$c = new C(); 
$c->testB(); 

Ja prawdopodobnie nie widząc coś, ale nie mogę znaleźć co. Czy ktoś mógłby mi wytłumaczyć to zachowanie?

+0

Bardzo dziwne zachowanie rzeczywiście. – Sherlock

+0

Czy chcesz wyjaśnić racjonalne uzasadnienie lub implementację tego zachowania? Ponieważ jest to PHP, istnieje duża szansa, że ​​* nie ma żadnego uzasadnienia. – Jon

+0

Jeśli istnieje uzasadnienie, chciałbym wiedzieć, co to jest. – Arjan

Odpowiedz

6

Możesz uzyskać dostęp do tych metod, ponieważ istnieje ich deklaracja jako chroniona w Foo, która jest Twoim rodzicem i która daje ci pozwolenie na dostęp do niej. Jeśli usuniesz deklarację od rodzica i zadeklarujesz metodę chronioną w B, otrzymasz błąd krytyczny.

To jest zgłaszane jako błąd w PHP https://bugs.php.net/bug.php?id=50892

+0

Nie możesz wywołać 'nowego B()' z 'A' jeśli' B' nadpisuje '__construct' nawet jeśli deklaruje to' Foo'. –

+2

Dzięki za skierowanie mnie na link bugs.php.net. Szukałem tam, ale nie mogłem znaleźć raportu o błędzie. – Arjan

1

Nie ma uzasadnienie o tym doniesiono 2 lata temu: https://bugs.php.net/bug.php?id=52120

+0

Dzięki za link.Myślę, że to dziwniejsze, że można wywoływać odziedziczone metody chronione niż niemożność wywoływania dziedziczonych konstruktorów (co jest oczekiwanym zachowaniem, imo) – Arjan