Изменения в обработке ссылок

Обзор

С точки зрения разработчика изменение, которое наиболее всего появлияет на устаревший код, это то, как обрабатываются ссылки, начиная с PHP 4.0.4.

До PHP 4.3 включительно, можно было отправлять, назначать или возвращать переменные по ссылке, которые на самом деле должны возвращены по значению, в виде константы, временного значения (например, результат выражения) или результат функции, которая сама возвращает по значению, как в данном случае:

<?php
$foo 
"123";

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

$bar = &return_value();
?>

Хотя данный код работает, как ожидается в PHP 4.3, в общем случае результат не определен. Движок Zend не мог корректно работать с этими значениями в качестве ссылок. Эта ошибка могла и приводила к различным трудно воспроизводимым ошибкам, связанных с проблемой повреждением памяти (memory corruption), особенно в случае большой кодовой базы.

В PHP 4.4.0, PHP 5.0.4 и в последующих версиях PHP, движок был исправлен, чтобы 'знать', когда операция со ссылкой используется на значении, на которое не следует ссылаться. В таких случаях используется фактическое значение, и выдается предупреждение. Предупреждение имеет уровень E_NOTICE в PHP 4.4.0 и выше и E_STRICT в PHP 5.0.4 и выше.

Код, который потенциально мог привести к повреждениям памяти, теперь больше к этому не приводит. Однако некоторый устаревший код может работать по-разному.

Код, который работает на PHP 4.3.x, но теперь терпит неудачу

<?php
function func(&$arraykey) {
    return 
$arraykey// функция возвращает по значению
}

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

var_dump($z);
?>

Выполнение вышеприведенного кода в любой версии PHP, до версии, когда ошибка со ссылками была исправлена, приведет к такому выводу:

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

После исправления ошибки этот же код приведет к следующему результату:

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

Это связано с тем, что после изменений функция func() присваивает переменной по значению. Значение $y переопределяется, и привязка ссылок сохраняется от $z. До исправления значение присваивалось по ссылке, что приводило к тому, что ссылка $y обновляется при каждом присваивании. Попытка привязки к временному значению по ссылке была причиной повреждения памяти.

Подобный код можно сделать одинаково работающим как в версиях PHP до исправления, так и после. Объвление func() может быть изменено для возврата по ссылке или присвоение по ссылке может быть может удалено из func().

<?php
function func() {
    return 
'возврат функции';
}

$x 'исходное значение';
$y =& $x;
$y = &func();
echo 
$x;
?>

В PHP 4.3 $x будет 'исходное значение', тогда как после исправления ошибки будет 'возврат функции' - помните, что если функция не возвращает по ссылке, присвоение по ссылке будет преобразовано в обычное присваивание. Опять же, этого можно достичь, заставив func() возвращать значение по ссылке, либо убрать присваивание по ссылке.

Код, который работает на PHP 4.3.x, но теперь вызывает ошибку

<?php
class Foo {

    function 
getThis() {
        return 
$this;
    }

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

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

В PHP 5.0.3 $bar выполняется как NULL вместо возврата объекта. Это происходит потому, что getThis() возвращает по значению, но значение присваивается по ссылке. Хотя он теперь работает как ожидается, это на самом деле некорректный код, который будет вызывать ошибку уровня E_NOTICE в PHP 4.4 или E_STRICT в PHP 5.0.4 и выше.

Код, который терпит неудачу на PHP 4.3.x, но теперь работает

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

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

В PHP 4.3 третий вызов var_dump() выдаст NULL из-за повреждения памяти, вызванного возвратом неинициализированного значения по ссылке. Это корректный код в PHP 5.0.4 и выше, но вызывает ошибки в ранних версиях PHP.

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

До PHP 5.0.5 невозможно было присвоить элемент массива таким образом reference in this way. Теперь это исправлено.

Код, который должен работать на PHP 5.0.x

Есть несколько примеров ошибок, указываемых в PHP 5.0 до исправлений ссылок, которые теперь 'работают'. Однако в обоих случаях ошибки генерируются в PHP 5.1.x, потому что код прежде всего некорректный. Возврат значений с помощью self:: теперь работает в общем случае, но выдает предупреждение E_STRICT, и хотя это может случиться при присвоении по ссылке на перегруженный объект, вы можете все еще увидеть E_ERROR при попытке использования, хотя само присваивание работает.

Предупреждения, которые ранее были, а теперь нет

Вложенные вызовы функций, возвращаемых по ссылке, являются корректным кодом как в PHP 4.3.x, так и в PHP 5.1.x, но вызывают ошибки E_NOTICE или E_STRICT в новых версиях PHP.

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

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

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