PHP: ссылки распределяются между рекурсивными вызовами - PullRequest
5 голосов
/ 27 ноября 2011

У меня есть три функции: foo, bar и baz, которые, с моей точки зрения, должны давать идентичные результаты. Тем не менее, я застрял с проблемой, что ссылки совместно используются между рекурсивными вызовами функций.

$array = array(
    'subs' => array(
        'a' => 1,
        'b' => 2,
    ),
);

function foo(&$array, $value, $callAgain = true) {
    $subs =& $array['subs'];
    foreach ($subs as &$sub)
        $sub = $value;
    if ($callAgain) {
        $copy = $array;
        foo($copy, $value + 1, false);
    }
}

function bar(&$array, $value, $callAgain = true) {
    foreach ($array['subs'] as &$sub)
        $sub = $value;
    if ($callAgain) {
        $copy = $array;
        bar($copy, $value + 1, false);
    }
}

function baz(&$array, $value, $callAgain = true) {
    foreach ($array['subs'] as $key => $sub)
        $array['subs'][$key] = $value;
    if ($callAgain) {
        $copy = $array;
        baz($copy, $value + 1, false);
    }
}

foo($array, 3);
var_dump($array);
bar($array, 3);
var_dump($array);
baz($array, 3);
var_dump($array);

Этот код дает следующие результаты:

array
  'subs' => 
    array
      'a' => int 4
      'b' => int 4
array
  'subs' => 
    array
      'a' => int 3
      'b' => int 4
array
  'subs' => 
    array
      'a' => int 3
      'b' => int 3

Однако я ожидаю, что все они вернут 3, 3, потому что копии массива передаются рекурсивным вызовам.

Как исправить первые две функции, чтобы они возвращали 3, 3? Я бы предпочел не использовать синтаксис функции baz, потому что она очень многословна.

1 Ответ

3 голосов
/ 27 ноября 2011

Я думаю, что вы ответили на свой вопрос - baz Это способ получить желаемое поведение. Две другие функции ведут себя так, как задумано в PHP, по крайней мере, в соответствии со страницей руководства на Что делает ссылка :

Обратите внимание, однако, что ссылки внутри массивов потенциально опасны. Выполнение нормального (не по ссылке) назначения со ссылкой на правой стороне не превращает левую сторону в ссылку, но ссылки в массивах сохраняются в этих обычных назначениях. Это также относится к вызовам функций, когда массив передается по значению. Пример:

/* Assignment of array variables */
$arr = array(1);
$a =& $arr[0]; //$a and $arr[0] are in the same reference set
$arr2 = $arr; //not an assignment-by-reference!
$arr2[0]++;
/* $a == 2, $arr == array(2) */
/* The contents of $arr are changed even though it's not a reference! */
?>

Другими словами, эталонное поведение массивов определяется поэлементно; эталонное поведение отдельных элементов отделено от эталонного статуса контейнера массива.

...