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";
?>
Modificări în manipularea referinţelor
