Facebook LinkedIn SourceForge Twitter RSS LastFM
logologo

"Only variables should be passed by reference"

Geoffray Warnants|12/02/2010|9 commentaires

Dans un environnement de développement normalement configuré pour afficher les erreurs de type E_NOTICE mais aussi E_STRICT, on se retrouve parfois confronté à une curieuse alerte, provoquée par une séquence d'instructions qui semble pourtant anodine.

Par exemple, pour extraire l'extension d'un nom de fichier, j'écris souvent de façon machinale :

$extension = end(explode('.', $filename));

Ce qui provoque une alerte assez déroutante :

Strict Standards: Only variables should be passed by reference

Pour comprendre ce message, il faut d'abord se rappeler que la fonction end() accepte en réalité une référence de tableau, ce qui est tout à fait compréhensible puisqu'elle va le modifier en positionnant le pointeur interne sur son dernier élément.

Ensuite [1], il faut savoir que lorsqu'une fonction retourne une valeur, une variable temporaire est créée. Généralement, on récupère cette variable temporaire en l'assignant à une autre variable déclarée explicitement, mais dans notre cas, la variable temporaire retournée par la fonction explode() est directement transmise à la fonction end() qui, bien que ce soit effectivement un Array, la refuse et provoque l'alerte.

Ce comportement tout à fait légitime ne date pas d'hier. Il a été introduit en PHP 5.0.5 afin d'éviter des corruptions mémoires. On peut certainement mieux comprendre ce risque en se souvenant de la différence significative entre les pointeurs et les références du C++.

Selon Rasmus Lerdorf, fondateur de PHP, il est recommandé d'effectuer l'opération en deux étapes, en transitant par une variable déclarée.

$split = explode('.', $filename);
$extension = end($split);

Il souligne aussi à très juste titre que dans ce cas bien précis qui consiste en une opération fondamentale sur une chaîne de caractères, il est absolument injustifié de passer par un tableau pour résoudre ce problème alors que des fonctions spécifiquement dédiées aux chaînes de caractères suffisent. J'ajouterai même qu'elles seront aussi plus performantes !

$extension = substr(strrchr($filename, '.'), 1);

J'ai cependant découvert que la fonction current() ne provoque pas d'erreur avec une variable temporaire alors que son prototype signale aussi un passage par référence. On peut donc supposer, en sachant que current() ne modifie pas le pointeur interne du tableau, que le message d'erreur généré par PHP n'est pas tout à fait pertinent. Il devrait plutôt mentionner "Only variables should be updated by reference". Rasmus, si tu pouvais confirmer... ;)

Sachant cela, les fanatiques qui voudraient à tout prix s'en tirer avec une seule ligne de code peuvent toujours le faire, au prix d'une opération certainement assez coûteuse :

$extension = current(array_reverse(explode('.', $filename)));

Notes

  1. Je me base ici sur mes lointaines et médiocres connaissances des compilateurs C++ pour me faire une idée de la popote interne de PHP, qui doit probablement fonctionner de façon assez similaire, puisque écrit en C. A vérifier dans le code source... par un expert ;)

Liens utiles

<<< Retour

Vos commentaires

9 commentaires postés

TOTO
20/02/2014 14:20Posté par TOTO
Merci beaucoup !
Nassim
11/10/2013 18:49Posté par Nassim
Thanks man !
David
26/06/2013 17:43Posté par David
Merci pour ce topic, on en apprend tous les jours.
Jeux
14/03/2013 14:30Posté par Jeux
Merci erwan $ext = pathinfo($filename, PATHINFO_EXTENSION); fonctionne tres bien
erwan
08/02/2013 16:07Posté par erwan
Belle démonstration ! (clapclap)

Je vois régulièrement cette ligne de code pour récupérer l'extension d'un fichier.

Je me demande réellement pourquoi personne n'utilise la fonction spécifique, à savoir : $ext = pathinfo($filename, PATHINFO_EXTENSION); ???
ahmed
03/01/2013 18:19Posté par ahmed
bravo
JB
12/11/2012 10:52Posté par JB
merci pour cette explication limpide
ammonite
11/07/2011 10:58Posté par ammonite
Merci!
Maloupi
30/03/2010 15:02Posté par Maloupi
Merci pour l'explication très claire et qui correspondait parfaitement à mon problème puisque j'utilisais aussi $extension = end(explode('.', $filename));

Réagir à cet article

*


(Ne sera pas publiée, servira uniquement à afficher votre gravatar)


(Lien en dur et dofollow)

zend framework