PHP foreachでの参照渡しに潜む罠
PHPforeachでの参照渡しに潜む罠
foreachでループ中に配列の値を変更したいとき参照渡しにしたりしますよね
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
var_dump($array);
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
var_dump($array);
array(3) { [0]=> string(3) "FOO" [1]=> string(3) "BAR" [2]=> &string(3) "BAZ"}
ここまで想定通り
ではこの配列にもう一度foreachを、今度は非参照渡しで使うと?
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
var_dump($array);
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
var_dump($array);
array(3) { [0]=> string(3) "foo" [1]=> string(3) "bar" [2]=> &string(3) "bar"}
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
var_dump($array);
array(3) { [0]=> string(3) "foo" [1]=> string(3) "bar" [2]=> &string(3) "bar"} !?
配列が破壊されてしまう
どうしてこうなった
$valueがforeachを抜けた後も参照を保持していることが原因
どうしてこうなった
$valueがforeachを抜けた後も参照を保持していることが原因
どうしてこうなった
「いまいちよくわからない」 (東京都 会社員 T・H)
$valueがforeachを抜けた後も参照を保持していることが原因
どうしてこうなった
「いまいちよくわからない」 (東京都 会社員 T・H)
「こころにひびかない」 (東京都 会社員 T・H)
$valueがforeachを抜けた後も参照を保持していることが原因
どうしてこうなった
「いまいちよくわからない」 (東京都 会社員 T・H)
「こころにひびかない」 (東京都 会社員 T・H)
「おすしがたべたい」 (東京都 会社員 T・H)
実際何が起こっているのか
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// 各ループの処理開始前に何が代入されているのか?
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// 各ループの処理開始前に何が代入されているのか?// $value = &$array[0] = ‘foo’// $value = &$array[1] = ‘bar’// $value = &$array[2] = ‘baz’
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// 各ループの処理開始前に何が代入されているのか?// $value = &$array[0] = ‘foo’// $value = &$array[1] = ‘bar’// $value = &$array[2] = ‘baz’// $valueを変更して参照先の$array[n]を変更している
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// ループ終了後に$valueはどうなっているのか?
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// ループ終了後に$valueはどうなっているのか?// $value = &$array[2] = ‘BAZ’
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// ループ終了後に$valueはどうなっているのか?// $value = &$array[2] = ‘BAZ’// これは$array[2]への参照なので、// $valueを変更すると$array[2]が変更される
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// $value = &$array[2] = ‘BAZ’
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// $value = &$array[2] = ‘BAZ’
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
// 各ループの処理開始前に何が代入されているのか?
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// $value = &$array[2] = ‘BAZ’
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
// 各ループの処理開始前に何が代入されているのか?// ($value = &$array[2]) = ($array[0] = ‘FOO’)
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// $value = &$array[2] = ‘BAZ’
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
// 各ループの処理開始前に何が代入されているのか?// ($value = &$array[2]) = ($array[0] = ‘FOO’)// ($value = &$array[2]) = ($array[1] = ‘BAR’)
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// $value = &$array[2] = ‘BAZ’
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
// 各ループの処理開始前に何が代入されているのか?// ($value = &$array[2]) = ($array[0] = ‘FOO’)// ($value = &$array[2]) = ($array[1] = ‘BAR’)// ($value = &$array[2]) = ($array[2] = ‘BAR’)
$array = [‘foo’, ‘bar’, ‘baz’]foreach ($array as &$value) { $value = strtoupper($value);}
// $value = &$array[2] = ‘BAZ’
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
var_dump($array);
array(3) { [0]=> string(3) "foo" [1]=> string(3) "bar" [2]=> &string(3) "bar"}
回避するために
二回目も参照渡しにする
書くタイミングがずれていると忘れる危険性が高い
間に大量のコードがあると忘れる危険性が高い
別の人間が書くならば尚更
二回目も参照渡しにする
書くタイミングがずれていると忘れる危険性が高い
間に大量のコードがあると忘れる危険性が高い
別の人間が書くならば尚更
そもそも、「この後はこういうふうに書かないと壊れます」などという書き方自体すべきではない
二回目も参照渡しにする
ループを抜けたらunset($value)する
実はマニュアルでも推奨されているhttp://php.net/manual/ja/control-structures.foreach.php
ループを抜けたらunset($value)する
実はマニュアルでも推奨されているhttp://php.net/manual/ja/control-structures.foreach.php
書き忘れる危険性はある
ループを抜けたらunset($value)する
参照渡ししない
そもそも参照渡ししなければ当然起こらない
参照渡ししない
そもそも参照渡ししなければ当然起こらない
参照渡ししない
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
$array_lower = [];foreach ($array as $value) { $array_lower[] = strtolower($value);}
そもそも参照渡ししなければ当然起こらない
参照渡ししない
foreach ($array as $key => $value) { $array[$key] = strtolower($value);}
$array_lower = [];foreach ($array as $value) { $array_lower[] = strtolower($value);}
確実だがちょっと書くのが面倒な場合もある
わかりにくいバグの温床になるので気をつけましょう