Changement dans la gestion des références
Présentation
Du point de vue du programmeur, le changement qui aura le plus d'impact sur le code déjà écrit est la manière avec laquelle les références sont gérées dans les versions PHP 4.4.0 et plus récentes.
Jusqu'à la version PHP 4.3 incluse, il était possible d'envoyer, assigner ou retourner des variables par références, alors qu'elles auraient dues être retournées par valeur, comme des constantes, des valeurs temporaires (comme le résultat d'une expression), ou le résultat d'une fonction qui elle-même, retourne une valeur.
<?php $foo = "123"; function return_value() { global $foo; return $foo; } $bar = &return_value(); ?>
Même si ce code fonctionne tel qu'attendu en PHP 4.3, en général il conduit à une situation indéfinie. Le moteur Zend ne peut pas fonctionner correctement avec des valeurs passées par références. Ce bogue peut et a conduit à des corruptions de mémoire difficiles à reproduire, notamment lorsque le code de l'application est compliqué.
En PHP 4.4.0, PHP 5.0.4 et toutes les versions plus récentes, le moteur
Zend a été modifié pour reconnaître les cas où une valeur ne doit pas
être utilisée par référence. La valeur réelle est maintenant utilisée
dans ces cas, et une alerte est émise. Cette alerte prend la forme d'un
message E_NOTICE
en PHP 4.4.0 et plus récent, et
E_STRICT
en PHP 5.0.4 et plus récent.
Du code qui pouvait produire des corruptions de mémoire ne peut plus le faire. Cependant, certains codes anciens peuvent fonctionner de manière différente.
Code qui fonctionnait en 4.3.x, mais qui échoue maintenant
<?php function func(&$arraykey) { return $arraykey; // la fonction retourne par valeur! } $array = array('a', 'b', 'c'); foreach (array_keys($array) as $key) { $y = &func($array[$key]); $z[] =& $y; } var_dump($z); ?> <
L'exécution du script ci-dessus dans une version de PHP antérieure à la correction du bogue produit ce résultat :
array(3) { [0]=> &string(1) "a" [1]=> &string(1) "b" [2]=> &string(1) "c" }
Après la correction du bogue, cela donne :
array(3) { [0]=> &string(1) "c" [1]=> &string(1) "c" [2]=> &string(1) "c" }
Ceci est dû au fait que func() effectue une assignation par valeur. La valeur de $y est réassignée, et que la liaison par référence avec $z est préservée. Avant la correction du bogue, la valeur était assignée par référence, faisant que la variable $y était réassignée à chaque assignement. La tentative de liaison avec une valeur temporaire est la cause de la corruption de la mémoire.
Ce code peut être réparé pour fonctionner à l'identique avec des versions pré et post-correction. La signature de func() peut être modifiée pour retourner les valeurs par référence, ou bien l'affectation par référence peut être supprimée du résultat de func().
<?php function func() { return 'function return'; } $x = 'original value'; $y =& $x; $y = &func(); echo $x; ?>
En PHP 4.3, $x vaudrait 'original value', alors qu'après le changement, il vaudrait 'function return' : n'oubliez pas que la fonction ne retourne plus par référence, et que la référence est convertie en affectation par valeur. Encore une fois, cela peut être corrigé en forçant func() à retourner par référence, ou bien en éliminant l'affectation par référence.
Code qui fonctionnait en PHP 4.3.x, mais qui émet maintenant une erreur
<?php
class Foo {
function getThis() {
return $this;
}
function destroyThis() {
$baz =& $this->getThis();
}
}
$bar = new Foo();
$bar->destroyThis();
var_dump($bar);
?>
En PHP 5.0.3, $bar vaut NULL
au
lieu de l'objet attendu. Cela arrive car getThis()
retourne une valeur, mais que la valeur est assignée par référence.
Même si cela fonctionne désormais comme attendu, ce code est en
fait invalide, et émettra une alerte E_NOTICE
sous
PHP 4.4 ou E_STRICT
en PHP 5.0.4 et plus récent.
Code qui échouaient en PHP 4.3.x, mais qui fonctionne maintenant
<?php
function &f() {
$x = "foo";
var_dump($x);
print "$x\n";
return($a);
}
for ($i = 0; $i < 3; $i++) {
$h = &f();
}
?>
En PHP 4.3, le troisième appel à var_dump() produit
NULL
, à cause d'une correction de la mémoire, causée
par le retour d'une valeur non initialisée par référence. C'est du code
valide en PHP 5.0.4 et plus récent, mais il produit une erreur dans les
versions plus anciennes.
<?php $arr = array('a1' => array('alfa' => 'ok')); $arr =& $arr['a1']; echo '-'.$arr['alfa']."-\n"; ?>
Jusqu'en PHP 5.0.5, il n'était pas possible d'assigner un élément de tableau par référence de cette manière. C'est corrigé maintenant.
Code qui aurait du fonctionner en PHP 5.0.x
Il y a quelques cas de bogues rapportés en PHP PHP 5.0 avant la correction
de ce bogue qui 'refonctionnent'. Cependant, dans tous les cas, des erreurs
sont émises en PHP 5.1.x, car le code est invalide. Retourner des références
avec self:: fonctionne maintenant, mais émet une alerte
E_STRICT
, et même si votre expérience est différente
lors de l'assignation d'un objet par référence, vous verrez toujours une
erreur E_ERROR
lorsque vous tentez cela même si
l'assignation semble fonctionner.
Alertes qui vont et viennent
Des appels imbriqués à des fonctions retournant des valeurs par référence
sont valides en PHP 4.3.x et PHP 5.1.x, mais ils émettent des erreurs
E_NOTICE
et E_STRICT
dans les
nouvelles versions de PHP.
<?php function & foo() { $var = 'ok'; return $var; } function & bar() { return foo(); } $a =& bar(); echo "$a\n"; ?>