PHP 7.1.0 Release Candidate 3 Released

Cambiamenti nella gestione dei riferimenti

Panoramica

Dal punto di vista di chi scrive script PHP, il cambiamento più importante rispetto al codice obsoleto è nel modo in cui vengono gestiti i riferimenti in tutte le versioni di PHP dopo il rilascio di PHP 4.4.0.

Fino a PHP 4.3, era possibile inviare, assegnare o restituire variabili per riferimento che in realtà dovevano essere restituite per valore, come una costante, un valore temporaneo (ad esempio il risultato di un'espressione), o il risultato di una funzione che era stato a sua volta ritornato per valore, come in questo caso:

<?php
$foo 
"123";

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

$bar = &return_value();
?>

Anche se il codice di solito funziona come previsto sotto PHP 4, nel caso generale il risultato è indefinito. Il Zend Engine potrebbe non agire correttamente su questi valori come riferimenti. Questo bug poteva e ha portato a diversi problemi di corruzione della memoria difficili da riprodurre, in particolare quando il codice era di grandi dimensioni.

In PHP 4.4.0, PHP 5.0.4 e tutte le versioni di PHP successive, il motore è stato sistemato in modo da 'sapere' quando l'operazione di riferimento viene utilizzata su un valore che non deve essere referenziato. Il valore reale viene ora utilizzato in tali casi, e viene emesso un avvertimento. L'avvertimento assume la forma di un E_NOTICE in PHP 4.4.0 o versione superiore, and E_STRICT in PHP 5.0.4 o versione superiore.

Il codice che potrebbe portare ad una corruzione della memoria non può più farlo. Tuttavia, alcuni script obsoleti potrebbero funzionare in modo diverso come risultato.

Il codice che funzionava in PHP 4.3, ma ora non più

<?php
function func(&$arraykey) {
    return 
$arraykey// la funzione restituisce per valore!
}

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

var_dump($z);
?>
<

L'esecuzione dello script precedente sotto qualsiasi versione di PHP che precede la correzione di riferimento produrrebbe questo output:

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

Dopo la correzione di riferimento, lo stesso codice riporterebbe come output:

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

Questo perchè, a seguito delle modifiche, func() assegna per valore. Il valore di $y è riassegnato, e il riferimento al binding è preservato da $z. Prima della correzione, il valore era assegnato per riferimento, portando $y ad essere ri-bound-ato su ogni assegnazione. Il tentativo di effettuare il binding ad un valore temporaneo per riferimento era la causa della corruzione della memoria.

Tale codice può essere fatto funzionare in modo identico in entrambe le versioni PHP prima della correzione e dopo la correzione. La signature di func() può essere modificata per restituire per riferimento, o l'assegnazione di riferimento può essere rimossa dal risultato di func().

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

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

In PHP 4.3 $x sarebbe 'original value', mentre dopo i cambiamenti sarebbe stato 'function return' - ricordando che dove la funzione non restituisce per riferimento, l'assegnazione di riferimento viene convertita in una regolare assegnazione. Ancora una volta, questo può essere portato ad una base comune, sia forzando func() a restituire per riferimento o eliminando l'assegnazione per riferimento.

Il codice che funzionava in PHP 4.3.x, ma che ora genera un errore

<?php
class Foo {

    function 
getThis() {
        return 
$this;
    }

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

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

In PHP 5.0.3, $bar veniva valutato in NULL invece di restituire un oggetto. Questo accadeva perchè getThis() restituisce per valore, ma il valore qui viene assegnato per riferimento. Anche se ora funziona nel modo previsto, questo è in realtà un codice invalido che lancerà un avviso E_NOTICE sotto PHP 4.4 o un avviso E_STRICT sotto PHP 5.0.4 e successivi.

Il codice che non funzionava in PHP 4.3.x, ma che ora funziona

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

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

In PHP 4.3 la terza chiamata a var_dump() produce NULL, a causa della corruzione della memoria causata dalla restituzione di un valore non inizializzato per riferimento. Questo codice è valido in PHP 5.0.4 e successivo, ma lanciava errori nelle precedenti versioni di PHP.

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

Fino a PHP 5.0.5, non era possibile assegnare un elemento di un array per riferimento in questo modo. Ora invece è possibile.

Codice che dovrebbe aver funzionato sotto PHP 5.0.x

Ci sono un paio di casi di bug riportati sotto PHP 5.0 prima delle correzioni di riferimento sul quale ora 'funziona'. Tuttavia, in entrambi i casi vengono generati degli errori da PHP 5.1.x, perché il codice non è valido, in primo luogo. Ritornando i valori per riferimento utilizzando self:: ora funziona in casi generali, ma genera un avvertimento E_STRICT, e anche se la propria situazione può variare durante l'assegnazione per riferimento ad un oggetto sovraccaricato, si continuerà a vedere un E_ERROR quando si proverà, anche quando l'assegnazione stessa sembra funzionare.

Avvertimenti che andavano e venivano

Chiamate annidate a funzioni che ritornano per riferimento sono codice valido sia sotto PHP 4.3.x che sotto PHP 5.1.x, ma lanciano un messaggio E_NOTICE o E_STRICT ingiustificato sotto le versioni di PHP intermedie.

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