52 votes

Les parenthèses modifient la sémantique du résultat de l'appel de fonction

Il a été noté dans une autre question que l'encapsulation du résultat d'un appel de fonction en PHP entre parenthèses peut, d'une manière ou d'une autre, convertir le résultat en une expression à part entière, de sorte que ce qui suit fonctionne :

``

J'essaie de trouver dans la documentation une explication explicite et sans ambiguïté de ce qui se passe ici. Contrairement au C++, je ne connais pas suffisamment la grammaire PHP et son traitement des instructions/expressions pour le déduire moi-même.

Y a-t-il quelque chose de caché dans la documentation concernant ce comportement ? Sinon, quelqu'un d'autre peut-il l'expliquer sans recourir à la supposition ?


Mise à jour

J'ai d'abord trouvé cette EBNF prétendant représenter la grammaire PHP, et j'ai essayé de décoder mes scripts moi-même, mais j'ai finalement abandonné.

Ensuite, en utilisant phc pour générer un fichier .dot des deux variantes de foo(), j'ai produit des images AST pour les deux scripts en utilisant les commandes suivantes :

$ yum install phc graphviz
$ phc --dump-ast-dot test1.php > test1.dot
$ dot -Tpng test1.dot > test1.png
$ phc --dump-ast-dot test2.php > test2.dot
$ dot -Tpng test2.dot > test2.png

Dans les deux cas, le résultat était exactement le même :

Arbre syntaxique de snippets 1 et 2

``

32voto

NikiC Points 47270

Ce comportement pourrait être classé comme bug, vous ne devriez donc certainement pas vous en servir.

Les conditions (simplifiées) pour que le message ne soit pas lancé lors d'un appel de fonction sont les suivantes (voir la définition de l'opcode ZEND_SEND_VAR_NO_REF):

  • l'argument n'est pas un appel de fonction (ou si c'est le cas, il retourne par référence), et
  • l'argument est soit une référence soit il a un compte de référence égal à 1 (s'il a un compte de référence égal à 1, il est transformé en référence).

Analysosns ces points plus en détail.

Le premier point est vrai (pas un appel de fonction)

En raison des parenthèses supplémentaires, PHP ne détecte plus que l'argument est un appel de fonction.

Lors de l'analyse d'une liste d'arguments de fonction non vide, il y a trois possibilités pour PHP:

  • Un expr_without_variable
  • Un variable
  • (Un & suivi d'une variable, pour la fonctionnalité de passsage par référence supprimée en temps d'appel)

En écrivant juste get_array() PHP le voit comme une variable.

(get_array()) d'autre part ne qualifie pas comme une variable. C'est un expr_without_variable.

Cela affecte finalement la manière dont le code est compilé, à savoir que la valeur étendue de l'opcode SEND_VAR_NO_REF n'inclura plus le drapeau ZEND_ARG_SEND_FUNCTION, qui est la manière dont l'appel de fonction est détecté dans l'implémentation de l'opcode.

Le deuxième point est vrai (le compte de référence est de 1)

À plusieurs points, le moteur Zend autorise des non-références avec un compte de référence égal à 1 là où des références sont attendues. Ces détails ne devraient pas être exposés à l'utilisateur, mais malheureusement ils le sont ici.

Dans votre exemple, vous retournez un tableau qui n'est pas référencé ailleurs. Si c'était le cas, vous obtiendriez toujours le message, c'est-à-dire que ce deuxième point ne serait pas vrai.

Donc l'exemple suivant très similaire ne fonctionne pas:

1voto

feeela Points 9901

A) Pour comprendre ce qui se passe ici, il est nécessaire de comprendre la gestion des valeurs/variables et des références de PHP (PDF, 1.2 Mo). Comme indiqué à plusieurs reprises dans la documentation : "les références ne sont pas des pointeurs"; et vous ne pouvez retourner que des variables par référence à partir d'une fonction - rien d'autre.

À mon avis, cela signifie que toute fonction en PHP renverra une référence. Mais certaines fonctions (internes à PHP) requièrent des valeurs/variables en tant qu'arguments. Maintenant, si vous imbriquez des appels de fonctions, l'intérieur renvoie une référence, tandis que l'extérieur attend une valeur. Cela entraîne l'erreur 'célèbre' E_STRICT "Only variables should be passed by reference".

$fileName = 'example.txt';
$fileExtension = array_pop(explode('.', $fileName));
// résultera en l'Erreur 2048: Only variables should be passed by reference in…

B) J'ai trouvé une ligne dans la description de la syntaxe PHP liée à la question.

expr_without_variable = "(" expr ")"

En combinaison avec cette phrase de la documentation : "En PHP, presque tout ce que vous écrivez est une expression. La façon la plus simple mais la plus précise de définir une expression est 'quelque chose qui a une valeur'.", cela me conduit à la conclusion que même (5) est une expression en PHP, qui évalue à un entier avec la valeur 5.

(Comme $a = 5 n'est pas seulement une affectation mais aussi une expression, qui évalue à 5.)

Conclusion

Si vous passez une référence à l'expression (...), cette expression retournera une valeur, qui pourra ensuite être passée en argument à la fonction extérieure. Si cela (ma ligne de pensée) est vrai, les deux lignes suivantes devraient fonctionner de manière équivalente:

// ce que j'ai utilisé pendant des années : (espaces ajoutés uniquement pour la lisibilité)
$fileExtension = array_pop( ( explode('.', $fileName) ) );
// vs
$fileExtension = array_pop( $tmp = explode('.', $fileName) );

Consultez également PHP 5.0.5: Fatal error: Only variables can be passed by reference; 13.09.2005

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X