Modificări în manipularea referințelor

Aspecte generale

Din punctul de vedere al programatorului script-urilor PHP, schimbarea care cel mai probabil va afecta codul-sursă vechi este modul în care sunt manipulate referințele în toate versiunile PHP ce au urmat după PHP 4.4.0.

Până la, și inclusiv în PHP 4.3 era posibil de a transmite, atribui sau întoarce variabile prin referință, care ar fi trebuit în mod real să fie întoarse prin valoare, cum ar fi o constantă, o valoare temporară (de ex. rezultatul unei expresii) sau rezultatul unei funcții care însăși a fost întoarsă prin valoare, ca în acest exemplu:

<?php
$foo 
"123";

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

$bar = &return_value();
?>

Cu toate că acest cod-sursă de obicei lucrează conform așteptărilor în PHP 4.3, în caz general rezultatul este nedefinit. Motorul Zend nu poate funcționa corect cu aceste valori în calitate de referințe. Această eroare putea, și într-adevăr a dus la diverse probleme de alterare a conținutului memoriei dificile de reprodus, în special acolo unde baza de cod-sursă era foarte mare.

În PHP 4.4.0, PHP 5.0.4 și toate versiunile ulterioare ale PHP Motorul a fost corectat pentru a 'ști' când operația de referință este utilizată asupra unei valori care nu ar trebui să fie referită. Acum în aceste cazuri este utilizată valoarea propriu-zisă și este emisă o preîntâmpinare. Preîntâmpinarea ia forma unui E_NOTICE în PHP 4.4.0 și ulterior, și E_STRICT în PHP 5.0.4 și ulterior.

Codul-sursă care potețial putea să producă alterarea memoriei acum nu mai poate face aceasta. Însă unele coduri-sursă vechi pot ca rezultat să funcționeze în mod diferit.

Codul-sursă care lucra în PHP 4.3, dar acum eșuează

<?php
function func(&$arraykey) {
    return 
$arraykey// funcția întoarce rezultatul prin valoare!
}

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

var_dump($z);
?>
<

Lansând în execuție script-ul de mai sus în orice versiune a PHP mai înainte de corectarea referințelor va produce următoarea ieșire:

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

După corectarea referințelor același cod-sursă va produce următoarele:

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

Aceasta se întâmplă din cauza că, după corectare, func() atribuie prin valoare. Valoarea variabilei $y este re-atribuită și legătura prin referință este păstrată prin $z. Înainte de corectare valoarea era atribuită prin referință, ce ducea la faptul că $y era realipit la fiecare atribuire. Încercarea de a alipi ceva prin referință la o valoare temporară cauza alterarea memoriei.

Un astfel de cod-sursă poate fi făcut să funcționeze identic în ambele versiuni PHP: înainte de corectare și după. Semnătura func() poate fi alterată ca să întoarcă valoarea prin referință, sau atribuirea prin referință poate fi eliminată din rezultatul func().

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

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

În PHP 4.3 $x ar fi fost 'valoarea originară', în timp ce după corectări aceasta va fi 'ceea ce întoarce funcția' - amintiți-vă că atunci când funcția nu întoarce valoarea prin referință, atribuirea prin referință este convertită într-o atribuire obișnuită. Iarăși, aceasta poate fi adus la un numitor comun prin forțarea func() să întoarcă valoarea prin referință, sau prin eliminarea atribuirii prin referință.

Codul-sursă care lucra în PHP 4.3.x, dar acum aruncă o eroare

<?php
class Foo {

    function 
getThis() {
        return 
$this;
    }

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

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

În PHP 5.0.3, $bar evalua în NULL în loc să întoarcă un obiect. Aceasta s-a întâmplat din cauza că getThis() întoarce prin valoare, dar aici valoarea e atribuită prin referință. Cu toate că acum script-ul funcționează conform așteptărilor, acesta este de fapt un cod invalid, care va arunca un E_NOTICE sub PHP 4.4, sau un E_STRICT sub PHP 5.0.4 și ulterior.

Codul-sursă care eșua în PHP 4.3.x, dar acum lucrează

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

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

În PHP 4.3 al treilea apel la var_dump() produce NULL, din cauza alterării memoriei cauzate de întoarcerea unei valori neinițializate prin referință. Acest cod-sursă este valid în PHP 5.0.4 și ulterior, dar aruncă erori în versiunile anterioare ale PHP.

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

Până la PHP 5.0.5, nu era posibil de a atribui valoarea unui element al tabloului prin referință, în acest mod. Acum este posibil.

Codul-sursă care ar fi trebuit să lucreze în PHP 5.0.x

Există vre-o două erori raportate în PHP 5.0 înainte de corectarea referințelor, care acum 'lucrează'. Însă în ambele cazuri erorile sunt aruncate de PHP 5.1.x, deoarece în primul rând codul-sursă era invalid. Întoarcerea valorilor prin referință utilizând self:: acum lucrează la general, dar aruncă o preîntâmpinare E_STRICT, și cu toate că rezultatele variază de la caz la caz la atribuirea prin referință la un obiect supraîncărcat, oricum veți vedea un E_ERROR atunci când încercați aceasta, chiar și acolo unde însăși atribuirea pare să funcționeze.

Preîntâmpinări care au apărut și au dispărut

Apeluri incluse unul în altul către funcții care întorc valoarea prin referință sunt exemple de cod-sursă valid în ambele PHP 4.3.x și PHP 5.1.x, dar aruncau erori nejustificate E_NOTICE sau E_STRICT în versiunile intermediare ale PHP.

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

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

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