2011年9月22日木曜日

PHPのforeachのちょっと嫌なところ



PHPのforeachの嫌なところが1つあります。


foreach ($a as $k => $v) { ... }の$kと$vが上書きされてしまうところです。




<?php
$name = 'hanachin';
$flat = array(101 => 'Kato', 102 => 'Yoshida', 201 => 'Sato');

echo "Hello, I'm $name", PHP_EOL;
foreach ($flat as $room_no => $name) {
// $name = $flat[$room_no]みたいな処理が入ってる(?)
echo "room #$room_no: $name", PHP_EOL;
}
echo "Hello, I'm $name", PHP_EOL;

# Output:
# Hello, I'm hanachin
# room #101: Kato
# room #102: Yoshida
# room #201: Sato
# Hello, I'm Sato
# --------
?>


$nameが上書きされてしまいました。


参照と組み合わせるともっと厄介です。


foreachで使った参照をunsetしないままforeachを使うと配列の最後の要素を意図せず書き換えてしまうことがあります。



<?php
$name = 'hanachin';
$flat = array(101 => 'Kato', 102 => 'Yoshida', 201 => 'Sato');

echo "Hello, I'm $name", PHP_EOL;
foreach ($flat as $room_no => &$name) {
// $name = &$flat[$room_no]みたいな処理が入ってる(?)
}
// foreachを抜けても$nameは$flatの最後の要素、$flat[201]への参照を持っている

foreach ($flat as $room_no => $name) {
// $name = $flat[$room_no]みたいな処理が入ってる(?)
// $nameは$flat[201]と同じところを指しているので
// $nameと$flat[201]の値が、$flat[$room_no]の値で上書きされていく
echo "room #$room_no: $name", PHP_EOL;
}
echo "Hello, I'm $name", PHP_EOL;

# Output:
# Hello, I'm hanachin
# room #101: Kato
# room #102: Yoshida
# room #201: Yoshida
# Hello, I'm Yoshida
# --------
?>


解決するためにはforeachでリファレンスを使ったら必ずその直後でunsetします。



<?php
$name = 'hanachin';
$flat = array(101 => 'Kato', 102 => 'Yoshida', 201 => 'Sato');

echo "Hello, I'm $name", PHP_EOL;
foreach ($flat as $room_no => &$name) {
}
unset($name); // 参照を削除

foreach ($flat as $room_no => $name) {
echo "room #$room_no: $name", PHP_EOL;
}
echo "Hello, I'm $name", PHP_EOL;

# Output:
# Hello, I'm hanachin
# room #101: Kato
# room #102: Yoshida
# room #201: Sato
# Hello, I'm Sato
# --------
?>


「foreach ($a as $k => $v) {}の$kと$vがforeachの中だけのスコープを持っていて、foreachの外の変数に影響及ぼさないと嬉しいのになぁ」と思いました。まる。


参考


PHP: foreach - Manual


PHP: 変数のスコープ - Manual





0 件のコメント:

コメントを投稿