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";
?>
LoadingChargement en cours