Eu apenas tive um comportamento muito estranho com um script php simples que estava escrevendo. Eu o reduzi ao mínimo necessário para recriar o bug:
<?php
$arr = array("foo",
"bar",
"baz");
foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);
foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?
?>
Isso gera:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
Isso é um bug ou algum comportamento realmente estranho que deveria acontecer?
foreach($x AS &$y){ ... unset($y); }
- ele está no php.net (não sei onde) porque é um erro muito cometido.Respostas:
Após o primeiro loop foreach,
$item
ainda é uma referência a algum valor que também está sendo usado por$arr[2]
. Portanto, cada chamada foreach no segundo loop, que não chama por referência, substitui esse valor e$arr[2]
, portanto , pelo novo valor.Então, faça um loop 1, o valor e
$arr[2]
torne - se$arr[0]
, que é 'foo'.Loop 2, o valor e
$arr[2]
tornar - se$arr[1]
, que é 'bar'.Loop 3, o valor e
$arr[2]
se tornar$arr[2]
, que é 'bar' (por causa do loop 2).O valor 'baz' é realmente perdido na primeira chamada do segundo loop foreach.
Depurando a saída
Para cada iteração do loop, ecoaremos o valor
$item
e imprimiremos recursivamente a matriz$arr
.Quando o primeiro loop é executado, vemos esta saída:
No final do loop,
$item
ainda está apontando para o mesmo local que$arr[2]
.Quando o segundo loop é executado, vemos esta saída:
Você notará como cada matriz de tempo coloca um novo valor
$item
, também é atualizado$arr[3]
com o mesmo valor, pois ambos ainda apontam para o mesmo local. Quando o loop atingir o terceiro valor da matriz, ele conterá o valorbar
porque foi definido apenas pela iteração anterior desse loop.Isso é um bug?
Não. Esse é o comportamento de um item referenciado, e não um bug. Seria semelhante a executar algo como:
Um loop foreach não é de natureza especial em que pode ignorar itens referenciados. É simplesmente definir essa variável para o novo valor sempre que você faria fora de um loop.
fonte
$item
não é uma referência a$arr[2]
, o valor contido por$arr[2]
é uma referência ao valor referido por$item
. Para ilustrar a diferença, você também pode desconfigurar$arr[2]
, e$item
não seria afetado, e escrever para$item
não afetá-la.$item
não sai do escopo quando o loop foreach é encerrado? Isso parece um problema de fechamento?$item
é uma referência$arr[2]
e está sendo substituída pelo segundo loop foreach, como apontou animuson.fonte
Embora isso possa não ser oficialmente um bug, na minha opinião é. Penso que o problema aqui é que temos a expectativa de
$item
sair do escopo quando o loop for encerrado, como ocorreria em muitas outras linguagens de programação. No entanto, isso não parece ser o caso ...Este código ...
Dá a saída ...
Como outras pessoas já disseram, você está substituindo a variável referenciada no
$arr[2]
seu segundo loop, mas isso só está acontecendo porque$item
nunca saiu do escopo. O que vocês acham ... bug?fonte
{}
blocos em geral. Isto é como a linguagem funcionaUma explicação mais fácil, parece de Rasmus Lerdorf, criador original do PHP: https://bugs.php.net/bug.php?id=71454
fonte
O comportamento correto do PHP deve ser um erro de AVISO na minha opinião. Se uma variável referenciada criada em um loop foreach for usada fora do loop, isso causará um aviso. Muito fácil se apaixonar por esse comportamento, muito difícil identificá-lo quando aconteceu. E nenhum desenvolvedor lerá a página de documentação do foreach, não é uma ajuda.
Você deve
unset()
consultar após o seu loop para evitar esse tipo de problema. unset () em uma referência apenas removerá a referência sem danificar os dados originais.fonte
isso é porque você usa pela diretiva ref (&). O último valor será substituído pelo segundo loop e corromperá sua matriz. a solução mais simples é usar um nome diferente para o segundo loop:
fonte