2017-07-10 64 views
7

Jak wspomniano on Reddit's LOL PHP sub, PHP 7 może używać rozszerzonej klasy lub klasy bazowej, odnosząc się do self, w przeciwieństwie do PHP 5, które zawsze odnosi się do rozszerzonej klasy.Na jakich warunkach samo PHP 7 odnosi się do klasy bazowej?

<?php 

class Foo { 
    const A = "FooA"; 
    const B = self::A . self::C; 
    const C = "FooC"; 

} 

class Bar extends Foo { 
    const A = "BarA"; 
    const C = "BarC"; 
} 

var_dump(Bar::B); 

Try it online

PHP 5

string(8) "BarABarC" 

PHP 7

string(8) "FooABarC" 

Zachowanie PHP 7 jest szczególnie niepokojące, ponieważ nie wydaje się być dowolną prostą zasadę wiedzieć gdy self odnosi się do klasy bazowej lub klasy rozszerzonej. Jakie są zasady określania, do której klasy odwoła się klasa self w PHP 7?

+0

https://stackoverflow.com/questions/10131786/how-does-self-exactly-work-in-inited-classes – mkaatman

+0

Z opublikowanego linku 3v7l wynika, że ​​nastąpiła zmiana w zachowaniu (jestem nie jest w stanie znaleźć żadnego uzasadnienia) między PHP 5.6 a PHP 7.0, a także błąd w PHP od wersji 7.0.0 do 7.1.3 ... Prawdopodobnie najlepiej, aby uniknąć takich konstrukcji ... –

+0

To zależy od tego gdzie B jest zdefiniowane. Przenieś definicję B dookoła i sprawdź wynik. Jeśli przejdziesz na dół, wyjścia dla wszystkich wersji 7.x.x są takie same. Przenieś to na górę i zauważysz różnicę. – puelo

Odpowiedz

2

self::powinny zawsze odnoszą się do klasy stosuje się go w (zauważ, że zachowanie PHP 5 jest źle zbyt.)

To był bug, fixed w 7.1.4, które stosuje się do uchwały self:: i parent:: tylko w obrębie stałych klasowych.

Zasadniczo w:

const B = self::A . self::C; 

self::C jest nadal nieznany w tym momencie, a rozdzielczość jest odroczona. W momencie ostatecznego rozwiązania problemu niestety utracono odpowiedni zakres.


Problemem jest również bardziej subtelne niż tylko podstawy vs przedłużony może skończyć się wartość z różne rozciągającą klasy. Na przykład.:

https://3v4l.org/cY1I0

class A { 
    const selfN = self::N; 
    const N = 'A'; 
} 

class B extends A { 
    const N = 'B'; 
} 

class C extends A { 
    const N = 'C'; 
} 

var_dump(B::selfN); // A 

var_dump(C::selfN); // A 

W PHP 7.0.0 7.1.3 to poprzez wyjścia:

string(1) "B" 
string(1) "B" 

Choć jeśli zamienić go na:

https://3v4l.org/RltQj

var_dump(C::selfN); // A 

var_dump(B::selfN); // A 

Dostaniesz:

string(1) "C" 
string(1) "C" 

Aby tego uniknąć w wersji dotkniętych użyć nazwy klasy zamiast self:: w klasie definicje stałych, na przykład const selfN = A::N

2

Oczekiwane zachowanie opisane w tym przykładzie jest niezdefiniowane. Odwołanie Foo do self::C dynamicznie odwołuje się do stałej przed jej zdefiniowaniem. Jeśli miałbyś wykonać Foo::B, oczekiwałbym, że rzuci ostrzeżenie.

Więcej kontekście zobaczyć ten „nie jest błąd” bug report:

LSB działa poprzez przechowywanie klasę o nazwie w ostatniej „rozmowy non-spedycyjnej” więc kluczem tutaj jest, aby zrozumieć, co do przekierowania call to: "Połączenie przychodzące" jest statyczne, wprowadzone przez self ::, parent ::, static ::.

LSB i jego śledzenie połączeń przekazywanych dalej są oddzielnymi mechanizmami od rozdzielczości klas wykonanych przez ::. Gdy użyjesz metody self :: method() lub static :: method(), klasa LSB pozostanie niezmieniona, jednak dwie (potencjalnie) rozstrzygną i wezmą inną metodę(). Innymi słowy, stan wewnętrzny jest taki sam, ale rzeczywisty kod wykonywany następny różni

Jednak interpreter zdecyduje się go obsługiwać, kodowanie coś w ten sposób w aplikacji w rzeczywistym świecie jest chyba zły pomysł.