Я пишу PHP-код, чтобы сделать некоторые преобразования каждого значения в массиве, а затем добавить некоторые значения в массив из внешнего источника (курсор MySQL или, скажем, другой массив). Если я использую foreach
и ссылку для преобразования значений массива
<?php $data = array('a','b','c'); foreach( $data as &$x ) $x = strtoupper($x); $extradata = array('d','e','f'); // actually it was MySQL cursor while( list($i,$x) = each($extradata) ) { $data[] = strtoupper($x); } print_r($data); ?>
( Здесь он находится в PHPfiddle )
чем данные повреждены. Так что я получаю
Array ( [0]=>A [1]=>B [2]=> [3]=>D [4]=>E [5] =>F )
вместо
Array ( [0]=>A [1]=>B [2]=>C [3]=>D [4]=>E [5] =>F )
Когда я не использую ссылку и пишу
foreach( $data as &$x ) $x = strtoupper($x);
Конечно, преобразование не происходит, но данные также не повреждены, поэтому я получаю
Array ( [0]=>a [1]=>b [2]=>c [3]=>D [4]=>E [5] =>F )
Если я пишу такой код
<?php $result = array(); $data1 = array('a','b','c'); foreach( $data1 as $x ) $result[] = strtoupper($x); $data2 = array('d','e','f'); // actually it was MySQL cursor while( list($i,$x) = each($data2) ) { $result[] = strtoupper($x); } print_r($result); ?>
все работает так, как ожидалось.
Array ( [0]=>A [1]=>B [2]=>C [3]=>D [4]=>E [5] =>F )
Конечно, копирование данных решает проблему. Но я хотел бы понять, какова странная проблема с этой ссылкой и как можно избежать таких проблем. Может быть, вообще плохо использовать ссылки PHP в коде (как многие говорят о C-указателях)?
Механизм ссылок на язык PHP имеет специфическую особенность, которая не является общей для других языков программирования. Общепризнано, что объект отражает все изменения, внесенные в его свойства посредством любой ссылки на него. Но присвоение самой ссылки либо запрещено, либо делает опорную точку для другого объекта. Вместо этого назначение ссылки в PHP заменяет весь базовый объект (объект, на который ссылается ссылка) на тот, который назначен. Так
$a = 1; $b = 2; $r = &$a; $r = $b; echo $a; // will output '2'
Это верно для присваивания, но не для unset
вызова, который не будет уничтожать базовый объект, но нарушает связь между ссылочным и заостренным объектами.
$a = 1; $b = 2; $r = &$a; unset($r); //! $r = $b; echo $a; // will output '1'
с$a = 1; $b = 2; $r = &$a; unset($r); //! $r = $b; echo $a; // will output '1'
В некоторых случаях это справочное поведение полезно, но часто его неправильно понимают, что приводит к таким проблемам, как показанный в вопросе.
Для проблем aviod с PHP-ссылками вы должны:
Таким образом, этот код будет работать
<?php $data = array('a','b','c'); foreach( $data as &$x ) $x = strtoupper($x); unset($x); $extradata = array('d','e','f'); // actually it was MySQL cursor while( list($i,$x) = each($extradata) ) { $data[] = strtoupper($x); } print_r($data); ?>
с<?php $data = array('a','b','c'); foreach( $data as &$x ) $x = strtoupper($x); unset($x); $extradata = array('d','e','f'); // actually it was MySQL cursor while( list($i,$x) = each($extradata) ) { $data[] = strtoupper($x); } print_r($data); ?>
Таким образом, следующий код будет работать тоже
<?php $data = array('a','b','c'); foreach( $data as &$x ) $x = strtoupper($x); $extradata = array('d','e','f'); // actually it was MySQL cursor while( list($i,$y) = each($extradata) ) { $data[] = strtoupper($y); }
Вы снова используете $x
в цикле над $extradata
, что приводит к тому, что ссылки становятся неустойчивыми.
Это работает:
$data = array('a','b','c'); foreach( $data as &$x ) $x = strtoupper($x); $extradata = array('d','e','f'); // actually it was MySQL cursor while( list($i,$anything_but_x) = each($extradata) ) { $data[] = strtoupper($anything_but_x); } print_r($data);
Это работает для меня …. Может быть, я мог бы сделать это по-другому, если бы знал, что вы пытаетесь сделать … но это должно сработать.
$data = array('a','b','c'); foreach( $data as &$x ) $x = strtoupper($x); $extradata = array('d','e','f'); // actually it was MySQL cursor foreach ($extradata as &$x) { $data[] = strtoupper ($x); }