2017-07-10 3 views
7

Comme noté on Reddit's LOL PHP sub, PHP 7 peut utiliser la classe étendue ou la classe de base en référence à self, contrairement à PHP 5 qui fait toujours référence à la classe étendue.Dans quelles conditions PHP 7 se réfère-t-il à la classe de base?

<?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" 

Le comportement de PHP 7 est particulièrement inquiétant car il ne semble pas être une règle simple de savoir lorsque self fait référence à la classe de base ou à la classe étendue. Quelles sont les règles pour déterminer à quelle classe référencera self en PHP 7?

+0

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

+0

Il semble d'après le lien posté 3v7l qu'il y avait un changement de comportement (je suis pas en mesure de trouver un raisonnement si) entre PHP 5.6 et PHP 7.0 et aussi un bug en PHP de la version 7.0.0 à la version 7.1.3 ... Probablement préférable d'éviter de telles constructions ... –

+0

Cela dépend de l'endroit où B est défini. Déplacez la définition de B autour et vérifiez la sortie. Si vous vous déplacez vers le bas, les sorties pour toutes les versions 7.x.x sont les mêmes. Déplacez-le vers le haut et vous remarquez une différence. – puelo

Répondre

2

self::devrait se référer toujours à la classe, il est utilisé dans (notez que le comportement de PHP 5 est faux aussi.)

Ce fut un bug, fixed dans 7.1.4, qui applique à la résolution de self:: et parent:: seulement dans les constantes de classe.

En gros, dans:

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

self::C est encore inconnue à ce stade et la résolution est reportée. Au moment de l'éventuelle résolution, la portée appropriée a malheureusement été perdue.


Le problème est plus subtil que simplement la base vs étendue que vous pouvez vous retrouver avec une valeur d'un autre classe extension. Par exemple.:

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 

En PHP 7.0.0 à travers 7.1.3 cette sortie:

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

Bien que si vous remplacez pour:

https://3v4l.org/RltQj

var_dump(C::selfN); // A 

var_dump(B::selfN); // A 

Vous obtiendrez:

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

Pour éviter cela dans les versions affectées utilisent le nom de la classe plutôt que self:: dans les définitions de classe, par exemple constante const selfN = A::N

2

Le comportement attendu décrit dans cet exemple est indéfini. Foo La référence à self::C fait référence dynamiquement à une constante avant qu'elle ne soit définie. Si vous deviez exécuter Foo::B, je m'attendrais à ce qu'il lève un avertissement.

Pour plus de contexte, voir ce « pas un bug » bug report:

LSB œuvres en stockant la classe nommée dans le dernier « appel non-renvoi » donc la clé ici est de comprendre ce qu'est une expédition call est: Un "forwarding call" est un appel statique introduit par self ::, parent ::, static ::. Le LSB et son suivi d'appel transféré sont des mécanismes distincts de la résolution de classe effectuée via ::. Lorsque vous utilisez self :: method() ou static :: method(), la classe LSB est inchangée, mais les deux vont (potentiellement) résoudre et appeler des méthodes différentes(). En d'autres termes, l'état interne est le même, mais le code réel en cours d'exécution suivant diffère

Cependant l'interprète choisit de le manipuler, quelque chose de codage de cette façon dans une application réelle est probablement une idée mauvaise.