PHP указатель и конфликт переменных - PullRequest
9 голосов
/ 26 октября 2011

У меня есть вопрос о PHP и использовании указателей и переменных.

Следующий код производит то, чего я не ожидал:

<?php
$numbers = array('zero', 'one', 'two', 'three');

foreach($numbers as &$number)
{
  $number = strtoupper($number);
}

print_r($numbers);

$texts = array();
foreach($numbers as $number)
{
  $texts[] = $number;
}

print_r($texts);
?>

Вывод следующий

Array
(
    [0] => ZERO
    [1] => ONE
    [2] => TWO
    [3] => THREE
)
Array
(
    [0] => ZERO
    [1] => ONE
    [2] => TWO
    [3] => TWO
)

Обратите внимание, что «ДВА» появляется дважды во втором массиве.

Кажется, что существует конфликт между двумя циклами foreach, каждый из которых объявляет переменную $ number (один раз по ссылке, а второй по значению).

Но почему? И почему это влияет только на последний элемент во втором foreach?

Ответы [ 3 ]

4 голосов
/ 26 октября 2011

Ключевым моментом является то, что в PHP нет указателей.Он имеет ссылки , что аналогично, но отличается концепцией, и есть некоторые тонкие различия.

Если вы используете var_dump () вместо print_r (), этолегче определить:

$collection = array(
    'First',
    'Second',
    'Third',
);

foreach($collection as &$item){
    echo $item . PHP_EOL;
}

var_dump($collection);

foreach($collection as $item){
    var_dump($collection);
    echo $item . PHP_EOL;
}

... печатает:

First
Second
Third
array(3) {
  [0]=>
  string(5) "First"
  [1]=>
  string(6) "Second"
  [2]=>
  &string(5) "Third"
}
array(3) {
  [0]=>
  string(5) "First"
  [1]=>
  string(6) "Second"
  [2]=>
  &string(5) "First"
}
First
array(3) {
  [0]=>
  string(5) "First"
  [1]=>
  string(6) "Second"
  [2]=>
  &string(6) "Second"
}
Second
array(3) {
  [0]=>
  string(5) "First"
  [1]=>
  string(6) "Second"
  [2]=>
  &string(6) "Second"
}
Second

Обратите внимание на символ &, оставленный в последнем элементе массива.

Для суммированиявверх, всякий раз, когда вы используете ссылки в цикле, рекомендуется удалить их в конце:

<?php

$collection = array(
    'First',
    'Second',
    'Third',
);

foreach($collection as &$item){
    echo $item . PHP_EOL;
}
unset($item);

var_dump($collection);

foreach($collection as $item){
    var_dump($collection);
    echo $item . PHP_EOL;
}
unset($item);

... печатает ожидаемый результат каждый раз.

2 голосов
/ 26 октября 2011

Вы должны разорвать ссылку после первого цикла.

foreach($numbers as &$number)
{
   $number = strtoupper($number);
}    
unset($number);

как указано в документации :

Предупреждение Ссылка на значение $ и последний элемент массива остаются даже после цикла foreach. Рекомендуется уничтожить его снята с охраны ().

Кроме того, если вы используете var_dump () вместо print_r (), вы заметите, что последний элемент массива после первого цикла является ссылкой:

array(4) {
[0]=>
string(4) "ZERO"
[1]=>
string(3) "ONE"
[2]=>
string(3) "TWO"
[3]=>
&string(5) "THREE"
}

Если вы следуете комментариям Стефана Герига по вопросу, есть ссылка, которая прекрасно объясняет это поведение: http://schlueters.de/blog/archives/141-References-and-foreach.html

2 голосов
/ 26 октября 2011

переменная $number инициализируется даже после цикла, необходимо разбить ссылку на unset

, этот код работает правильно:

<?php
$numbers = array('zero', 'one', 'two', 'three');

foreach($numbers as &$number)
{
  $number = strtoupper($number);
}

print_r($numbers);
unset($number);

$texts = array();
foreach($numbers as $number)
{
  $texts[] = $number;
}

print_r($texts);
?>

http://www.php.net/manual/en/language.references.unset.php

Когда вы отменяете ссылку, вы просто разрываете связь между именем переменной и содержимым переменной.Это не означает, что содержимое переменной будет уничтожено.

... воспринимайте это как аналог вызова Unix Unlink.

http://uk.php.net/manual/en/control-structures.foreach.php

Предупреждение о foreach

Ссылка на значение $ и последний элемент массива остаются даже после цикла foreach.Рекомендуется уничтожить его с помощью unset ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...