PHP Velho Oeste 2024

Cambios en el control de referencias

Resumen

Desde el punto de vista del lector de scripts PHP, el cambio que más impacto tendrá a la hora de mantener el código compatible, es la forma en que se manejan las referencias en las versiones de PHP posteriores a 4.4.0.

Hasta PHP 4.3, incluida ésta, era posible enviar, asignar o devolver variables por referencia que realmente tenían que devolver un valor, como una constante, un valor temporal (p.ej. el resultado de una expresión), o el resultado de una función que ha devuelto sí misma como valor, como aquí:

<?php
$foo 
"123";

function 
return_value() {
    global 
$foo;
    return 
$foo;
}

$bar = &return_value();
?>

Aunque este código normalmente funcionará en PHP 4.3 como cabe esperar, en general el resultado es indefinido. El Zend Engin podría no actuar correctamente con estos valores como referencia. Este error podría llevar a problemas de memorias corruptas difíciles de reproducir, en especial cuando el código base es muy largo.

En PHP 4.4.0, PHP 5.0.4, y posterior, el motor Zend se ha corregido para que 'sepa' cuándo una operación por referencia se usa en un valor que no debería referenciarse. En estos casos se utiliza el valor real, y se emite una alerta. Esta alerta es en forma de E_NOTICE en PHP 4.4.0 y superior, y de E_STRICT en PHP 5.0.4 y superior.

Los códigos que podrían producir corrupciones en memoria ya no podrán hacerlo. Sin embargo, hay códigos fuente que podrían funcionar de forma distinta a la esperada.

Código que funcionaba en PHP 4.3, pero ahora falla

<?php
function func(&$arraykey) {
    return 
$arraykey// ¡la función devuelve un valor!
}

$array = array('a''b''c');
foreach (
array_keys($array) as $key) {
    
$y = &func($array[$key]);
    
$z[] =& $y;
}

var_dump($z);
?>
<

Al ejecutar el script superior en alguna versión de PHP anterior a la corrección del error, producirá la siguiente salida:

array(3) {
  [0]=>
  &string(1) "a"
  [1]=>
  &string(1) "b"
  [2]=>
  &string(1) "c"
}

Y tras corregir el error de referencias, el mismo código resultará en:

array(3) {
  [0]=>
  &string(1) "c"
  [1]=>
  &string(1) "c"
  [2]=>
  &string(1) "c"
}

Esto es porque, de acuerdo a los cambios, la asignación en func() se hace por valor. El valor de $y se reasigna, y se impide enlazar por referencia a $z. Antes de la corrección, se asignaba el valor por referencia, dando lugar a que $y se revinculara en cada asignación. El intento de enlazar a un valor temporal por referencia era la causa de la memoria corrupta.

Dicho código se puede rescribir para funcionar de forma idéntica en ambas versiones. La definición de func() se puede modificar para devolver valores por referencia, o se podría eliminar la asignación por referencia del resultado de func().

<?php
function func() {
    return 
'devolución de la function';
}

$x 'valor original';
$y =& $x;
$y = &func();
echo 
$x;
?>

En PHP 4.3 $x será 'valor original', mientras que tras los cambios será 'devolución de la función' - recuerde que como la función no devuelve el valor por referencia, la asignación por referencia se convierte a una asignación convencional. De nuevo, esto puede se llevar a un comportamiento común, o bien forzando a que func() devuelva el valor por referencia o eliminando la asignación por referencia.

Código que funcionaba en PHP 4.3.x, pero ahora lanza un error

<?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 se evaluaba a NULL en lugar de devolver un objeto. Esto sucedía porque getThis() devuelve por valor, pero el valor aquí se asigna por referencia. A pesar de que funciona de la forma esperada, esto es en realidad un código inválido que lanzará un E_NOTICE en PHP 4.4 o un E_STRICT en PHP 5.0.4 y superior.

Código que fallaba en PHP 4.3.x, pero ahora funciona

<?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 la tercera llamada a var_dump() produce NULL, debido a la memoria corrupta provocada tras devolver por referencia un valor no inicializado. Este código es valido en PHP 5.0.4 y superior, pero lanza errores en versiones de PHP anteriores.

<?php
$arr 
= array('a1' => array('alfa' => 'ok'));
$arr =& $arr['a1'];
echo 
'-'.$arr['alfa']."-\n";
?>

Hasta PHP 5.0.5, no era posible asignar un array por referencia de esta forma. Ahora sí.

Código que debería haber funcionado en PHP 5.0.x

Hay un par de de errores en PHP 5.0, anteriores a que se corrigiera el problema de referencias, que ahora 'funcionan'. Sin embargo, en ambos casos los errores se emiten en PHP 5.1.x, porque en primer lugar, el código era inválido. Devolver un valor por referencia usando self:: ahora, en términos generales, funciona pero emite una alerta E_STRICT, y se emitirá un E_ERROR cuando se asigne por referencia un objeto sobrecargado, incluso cuando parezca que la asignación ha funcionado.

Alertas que vienen y van

Las llamadas anidadas a funciones que devuelven por valores por referencia son válidas tanto en PHP 4.3.x como en PHP 5.1.x, pero emiten E_NOTICE o E_STRICT en las versiones de PHP intermedias.

<?php
function & foo() {
    
$var 'ok';
    return 
$var;
}

function & 
bar() {
    return 
foo();
}

$a =& bar();
echo 
"$a\n";
?>
add a note add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top