どもです。本日のPHPの失敗談。
やりたかったのは、端的に示すとこういうもの。
$parent = array(
0 => 'a',
1 => 'b',
2 => 'c',
);
foreach($parent as &$child){
preg_replace("/[^-+0-9]/", '', $child);
}
$new_array = array();
foreach($parent as $id => $child){
$new_array[$id] = $child;
}
print_r($new_array);
この実行結果について、あるべき結果は、こう。
Array(
[0] => a
[1] => b
[2] => c
)
実行した結果が、これ。
Array(
[0] => a
[1] => b
[2] => b
)
...... why?????
色々と試してみた結果、以下の記述ならそれぞれあるべき姿になることが判明。
foreach($parent as $id => $child){
$parent[$id] = preg_replace("/[^-+0-9]/", '', $child);
}
$new_array = array();
foreach($parent as $id => $new_child){
$new_array[$id] = $new_child;
}
foreach($parent as &$child){
preg_replace("/[^-+0-9]/", '', $child);
}
unset($child);
$new_array = array();
foreach($parent as $id => $child){
$new_array[$id] = $child;
}
どうやら、foreachで参照渡しを行った後、その変数に参照の状態が残り続けていて、そのまま再利用しようとするとこのようにバグるらしい。
具体的な原理は、2回目のforeachのときの$childを$parent[2]に置き換えると分かり易い。
$new_array = array();
foreach($parent as $id => $parent[2]){
$new_array[$id] = $parent[2];
}
// ↓↓
$id = 0;
$parent[2] = $parent[0]; //$parent[0] = 'a' を代入するので、$parent[2] = 'a'
$new_array[0] = $parent[2];
$id = 1;
$parent[2] = $parent[1]; //$parent[1] = 'b' を代入するので、$parent[2] = 'a'→'b'
$new_array[1] = $parent[2];
$id = 0;
$parent[2] = $parent[2]; //$parent[2] = 'b' を代入するので、$parent[2] = 'b'→'b'
$new_array[2] = $parent[2];
なーーーーるほど。言われてみれば、とても納得。
だから配列の末尾だけひとつ前の値になって、あたかもポインタがズレたように見えたと。
改めて調べてみたら、foreach参照渡しバグ?はまあまあ有名なものらしいですね。
なんてこった。