C'est une question très valable.
1. Précédence entre [...]
Tout d'abord, il n'y a jamais d'ambiguïté quant à ce que PHP doit évaluer en premier en regardant le côté droit du [
, car le crochet nécessite un crochet de fermeture pour l'accompagner, et donc chaque opérateur entre a priorité sur le crochet ouvrant.
Exemple:
$a[1+2]
Le +
a priorité, c'est-à-dire que d'abord 1+2 doit être évalué avant que PHP puisse déterminer quel élément récupérer de $a.
Mais la liste de priorité des opérateurs ne concerne pas cela.
2. Associativité
Deuxièmement, il y a un ordre d'évaluation des paires consécutives de []
, comme ici:
$b[1][2]
PHP évaluera d'abord $b[1]
puis appliquera [2]
à cela. C'est une évaluation de gauche à droite et c'est ce qui est prévu avec une associativité à gauche.
Mais la question en jeu n'est pas tellement sur l'associativité, mais sur la priorité par rapport à d'autres opérateurs.
3. Précédence sur les opérateurs du côté gauche
La liste indique que les opérateurs clone
et new
ont priorité sur [
, et ce n'est pas facile à tester.
Tout d'abord, la plupart des structures où vous combineriez new
avec des crochets sont considérées comme une syntaxe invalide syntaxe. Par exemple, l'une de ces déclarations:
$a = new myClass()[0];
$a = new myClass[0];
donnera une erreur d'analyse:
erreur de syntaxe, '[' inattendu
PHP vous oblige à ajouter des parenthèses pour rendre la syntaxe valide. Nous ne pouvons donc pas tester les règles de priorité de cette manière.
Mais il y a une autre façon, en utilisant une variable contenant un nom de classe:
$a = new $test[0];
Cette syntaxe est valide, mais maintenant le défi est de créer une classe qui crée quelque chose qui agit comme un tableau.
Ce n'est pas trivial à faire, car une propriété d'objet est référencée de cette manière: obj->prop
, et non comme obj["prop"]
. On peut cependant utiliser la classe ArrayObject class qui peut gérer les crochets. L'idée est d'étendre cette classe et redéfinir la méthode offsetGet method pour s'assurer qu'un objet fraîchement créé de cette classe a des éléments de tableau à retourner.
Pour rendre les objets imprimables, j'ai fini par utiliser la méthode magique __toString, qui est exécutée lorsque l'objet doit être converti en chaîne.
J'ai donc mis en place ce système, en définissant deux classes similaires:
class T extends ArrayObject {
public function __toString() {
return "Je suis un objet T";
}
public function offsetGet ($offset) {
return "Je suis un élément de tableau d'un objet T";
}
}
class TestClass extends ArrayObject {
public function __toString() {
return "Je suis un objet de la classe TestClass";
}
public function offsetGet ($offset) {
return "Je suis un élément de tableau d'un objet de la classe TestClass";
}
}
$test = "TestClass";
Avec cette configuration, nous pouvons tester quelques choses.
Test 1
echo new $test;
Cette déclaration crée une nouvelle instance de TestClass, qui doit ensuite être convertie en chaîne, donc la méthode __toString est appelée sur cette nouvelle instance, ce qui renvoie:
Je suis un objet de la classe TestClass
C'est ce qu'on attend.
Test 2
echo (new $test)[0];
Ici, nous commençons par les mêmes actions, car les parenthèses forcent l'exécution de l'opération new
en premier. Cette fois, PHP ne convertit pas l'objet créé en chaîne, mais demande l'élément du tableau 0. Cette demande est répondue par la méthode offsetGet, et donc la déclaration ci-dessus affiche:
Je suis un élément de tableau d'un objet de la classe TestClass
Test 3
echo new ($test[0]);
L'idée est de forcer l'ordre d'exécution inverse. Malheureusement, PHP n'autorise pas cette syntaxe, nous devrons donc diviser la déclaration en deux afin d'obtenir l'ordre d'évaluation souhaité:
$name = $test[0];
echo new $name;
Alors maintenant, le [
est exécuté en premier, prenant le premier caractère de la valeur de $test, c'est-à-dire "T", et ensuite new
est appliqué à cela. C'est pourquoi j'ai aussi défini une classe T. L'appel de echo
à __toString sur cette instance, ce qui donne:
Je suis un objet T
Arrive maintenant le test final pour voir quel est l'ordre lorsqu'il n'y a pas de parenthèses:
Test 4
echo new $test[0];
Cette syntaxe est valide, et...
4. Conclusion
La sortie est:
Je suis un objet T
En fait, PHP a appliqué le [
avant l'opérateur new
, contrairement à ce qui est indiqué dans le tableau de priorités des opérateurs!
5. Comparaison de clone
avec new
L'opérateur clone
a un comportement similaire en combinaison avec [
. Étrangement, clone
et new
ne sont pas complètement égaux en termes de règles de syntaxe. Reproduire le test 2 avec clone
:
echo (clone $test)[0];
renvoie une erreur d'analyse:
erreur de syntaxe, '[' inattendu
Mais le test 4 répété avec clone
montre que [
a priorité sur lui.
@bishop a informé que cela reproduit le bug de documentation de longue date #61513: "La précédence de l'opérateur clone
est incorrecte".