PHP Velho Oeste 2024

Modificações na manipulação de referencias

Visão geral

Do ponto de vista do desenvolvedor PHP, as mudanças que mais impacta o código legado é a maneira que as referências são manipuladas em todas as versões posteriores ao PHP 4.4.0.

Até a versão 4.3, era possível enviar, definir ou retornar variáveis por referência o que deveria ser retornado por valor, como uma constante, um valor temporário (ex. o resultado de uma expressão), ou o resultado de uma função que retorna um valor, como abaixo:

<?php
$foo 
"123";

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

$bar = &return_value();
?>

Embora esse código deveria funcionar como esperado abaixo da versão 4.3 do PHP, na maioria dos casos o resultado é indefinido. A Zend Engine não pode efetivamente corrigir um desses valores como referência. Esse bug pode e levava a vários problemas de memória, particularmente onde a base de código é muito grande.

No PHP 4.4.0, PHP 5.0.4 e todos as versões subsequentes, a Engine foi corrigida para 'saber' quando a operação de referência é usada em um valor que não deveria ser referenciada. O valor atual agora é usado nesses casos, e um alerta é emitido. Esse alerta toma a forma de um E_NOTICE no PHP 4.4.0 e superior, e E_STRICT no PHP 5.0.4 e superior.

Código que pode potencialmente ocasionar problemas de memória agora não pode mais. Entretanto, algum código legado pode funcionar diferente por causa disso.

Código que funcionava no PHP 4.3, mas agora falha

<?php
function func(&$arraykey) {
    return 
$arraykey// function returns by value!
}

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

var_dump($z);
?>
<

Rodando o código acima em qualquer versão do PHP que precede a correção da referência irá retornar a seguinte saída:

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

Após a correção da referência, o mesmo código irá retornar o seguinte resultado:

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

Isso ocorre porque a função func() é por valor. O valor de $y é redefinido, e a referência é preservada de $z. Antes da correção, o valor foi atribuído por referência, levando $y para ser re-ligada a cada a cada atribuíção. A tentativa de ligar a cada valor de referência foi a causa do problema de memória.

O código pode ser feito para funcionar da mesma forma em ambas as versões do PHP antes e depois da correção. A definição da func() pode ser alterada para retornar por referência, ou a definição da referência pode ser removida do resultado da func().

<?php
function func() {
    return 
'function return';
}

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

No PHP 4.3 $x pode ser 'valor original', onde depois das alterações deverá ser 'o retorno da função' - lembre-se que onde a função não retorna por referência, a definição da referência é convertida em uma definição regular. Novamente, isso pode ser trazido a uma base comum, tanto forçando func() para retornar por referência, quanto eliminando a definição por referência.

Código que funcionava no PHP 4.3.x, mas agora dispara um erro

<?php
class Foo {

    function 
getThis() {
        return 
$this;
    }

    function 
destroyThis() {
        
$bar =& $this->getThis();
    }
}

$bar = new Foo();
$bar->destroyThis();
var_dump($bar);
?>

No PHP 5.0.3, $bar se torna NULL ao invés de retornar um objeto. Isso acontece porque getThis() retorna por valor, mas o valor aqui é definido por referência. Embora isso agora funcione da forma esperada, é na verdade um código inválido e irá disparar um E_NOTICE no PHP 4.4 e inferior ou um E_STRICT no PHP 5.0.4 e superior.

Código que funcionava no PHP 4.3.x, mas agora funciona

<?php
function &f() {
    
$x "foo";
    
var_dump($x);
    print 
"$x\n";
    return(
$a);
}

for (
$i 0$i 3$i++) {
    
$h = &f();
}
?>

No PHP 4.3 a terceira linha var_dump() retorna NULL, por causa do retorno de um valor não inicializado por referência. Esse cógido é valido no PHP 5.0.4 e superior, mas dispara um erro em versões mais antigas do PHP.

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

Antes do PHP 5.0.5, não era possível atribuír um array por referência dessa maneira. Agora é.

Código que deveria ter funcionado abaixo do PHP 5.0.x

Há um conjunto de bugs reportados abaixo do PHP 5.0.5 que antes das correções da referência que agora 'funcionam'. Entretanto, em ambos os casos erros são disparados pelo PHP 5.1.x, porque o código estava inválido desde o início. Retornar valores por referência usando self:: na maioria dos casos mas dispara um E_STRICT alerta, e apesar de seu tamanho poder variar quando atribuído por referência para um objeto sobrecarregado, você verá um E_ERROR quando você tentar, mesmo onde a própria atribuição parece funcionar.

Avisos que vem e vão

Chamadas aninhadas para funções que retornam por referência são códigos válidos para ambas as versões de PHP 4.3.x e PHP 5.1.x, mas disparam um aviso E_NOTICE ou E_STRICT nessas versões de PHP.

<?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