この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:12:00

タイトル: 繰り返し処理
SEOタイトル: PHP for / foreach / while の使い分けと無限ループ・多重ループ脱出

この記事の要点
  • for: 回数が決まっている時 for ($i = 0; $i < 10; $i++)
  • foreach: 配列やイテラブルの各要素を巡回。配列のキー・値が必要なら第一選択
  • while / do-while: 条件が真の間繰り返す。回数が事前に不明な処理向け
  • break / continue: ループ脱出と次イテレーション。break 2;2 重ループまで一気に抜ける
  • 性能: 単純配列 + キー不要なら for より foreach のほうが速い。count() をループ条件に書くと毎回呼ばれるので変数化する

for 文の基本構文

<?php
// for (初期化; 継続条件; 更新)
for ($i = 0; $i < 10; $i++) {
    echo $i . "\n";          // 0, 1, ..., 9
}

// 逆順
for ($i = 9; $i >= 0; $i--) {
    echo $i;
}

// 2 ずつ
for ($i = 0; $i < 20; $i += 2) {
    echo $i . " ";           // 0 2 4 ... 18
}

// 浮動小数も可能 (誤差注意)
for ($x = 0.0; $x < 1.0; $x += 0.1) {
    echo $x . "\n";
}

// 複数式 (カンマ区切り)
for ($i = 0, $j = 10; $i < $j; $i++, $j--) {
    // 中央で交差したら終了
}

count() をループ条件で呼ぶ罠

<?php
$arr = range(1, 1000);

// ❌ 毎回 count() が呼ばれて 1000 回呼ばれる (PHP 7+ は最適化されるが意図不明瞭)
for ($i = 0; $i < count($arr); $i++) {
    // ...
}

// ✅ 事前に変数化
$n = count($arr);
for ($i = 0; $i < $n; $i++) {
    // ...
}

// ✅ 配列なら foreach の方が早く意図も明確
foreach ($arr as $v) {
    // ...
}

foreach: 配列の標準

<?php
$fruits = ['apple', 'banana', 'cherry'];

// 値のみ
foreach ($fruits as $fruit) {
    echo $fruit . "\n";
}

// キーと値
foreach ($fruits as $i => $fruit) {
    echo "$i: $fruit\n";
}

// 連想配列
$user = ['name' => 'Alice', 'age' => 30, 'email' => 'a@example.com'];
foreach ($user as $key => $value) {
    echo "$key => $value\n";
}

// 参照渡しで要素を直接更新
$nums = [1, 2, 3];
foreach ($nums as &$n) {
    $n *= 2;
}
unset($n);                    // ★ 必ず参照を解除 (重要)
print_r($nums);               // [2, 4, 6]

// ネストした foreach
$matrix = [[1,2,3], [4,5,6], [7,8,9]];
foreach ($matrix as $row) {
    foreach ($row as $cell) {
        echo $cell . " ";
    }
    echo "\n";
}

for vs foreach の使い分け

条件推奨理由
配列の全要素を順番に処理foreach意図が明確、高速
キー(インデックス)を演算に使うfori+1 番目との比較などに便利
配列の途中まで / 途中からfor or array_slice + foreach柔軟性
数値範囲を扱う (回数指定)for明示的
反復可能オブジェクト (Iterator)foreach標準対応
無限ループ・条件次第で終了while条件が複雑な時に

while / do-while

<?php
// 条件が真の間繰り返す
$n = 1;
while ($n < 100) {
    $n *= 2;                  // 1, 2, 4, 8, 16, 32, 64, 128で抜ける
}

// do-while: 最低 1 回は実行
$pass = 'guest';
do {
    $pass = readline('Password: ');
} while ($pass !== 'secret');

// 行ごとに読み込む典型パターン
$fp = fopen('data.txt', 'r');
while (($line = fgets($fp)) !== false) {
    echo $line;
}
fclose($fp);

break / continue で制御

<?php
// break: ループを抜ける
foreach ($users as $u) {
    if ($u->isAdmin()) {
        $admin = $u;
        break;                // 最初の管理者で終了
    }
}

// continue: 次のイテレーションへ
foreach ($scores as $s) {
    if ($s < 0) continue;     // 負の値はスキップ
    $total += $s;
}

// break/continue にレベル指定 (PHP 独自)
foreach ($matrix as $row) {
    foreach ($row as $cell) {
        if ($cell === 'X') {
            break 2;          // ★ 2 重ループを一気に脱出
        }
    }
}

// 内側の continue 2 で外側の次イテレーションへ
foreach ($groups as $group) {
    foreach ($group->items as $item) {
        if ($item->skip) continue 2;  // 外側のループの次の group へ
        // ...
    }
}

無限ループの書き方と回避

<?php
// 意図的な無限ループ
while (true) {
    $line = fgets(STDIN);
    if ($line === false || trim($line) === 'quit') break;
    echo "echo: $line";
}

// for で同じこと
for (;;) {
    // ...
    if ($done) break;
}

// ❌ 意図しない無限ループ: 更新忘れ
$i = 0;
while ($i < 10) {
    echo $i;
    // $i++; を忘れた → 永遠に動く
}

// ❌ 浮動小数誤差で抜けられない
for ($x = 0.0; $x !== 1.0; $x += 0.1) {
    // 0.99999... のため永遠に等しくならない
}
// ✅ <= で
for ($x = 0.0; $x < 1.0 + 1e-9; $x += 0.1) { ... }

// 暴走対策のセーフティ
$maxIter = 10000;
$i = 0;
while ($conditionsNotMet) {
    if (++$i > $maxIter) throw new RuntimeException('iteration limit');
    // ...
}

foreach の参照渡し: ありがちなバグ

<?php
$arr = [1, 2, 3];

// 1 回目: 参照で全要素を 2 倍
foreach ($arr as &$v) {
    $v *= 2;
}
// この時点で $arr = [2, 4, 6]、$v はまだ $arr[2] への参照

// ❌ unset($v) を忘れて 2 回目の foreach (値渡し)
foreach ($arr as $v) {
    // ループ内で $v に代入 → $arr[2] が書き換えられる!
}
// $arr が壊れる: [2, 4, 4] になる

// ✅ 必ず unset
foreach ($arr as &$v) { $v *= 2; }
unset($v);                            // 必須

性能比較 (参考)

<?php
// 100 万要素配列の合計
$arr = range(1, 1_000_000);

// 1. foreach 値のみ (最速)
$t = microtime(true); $sum = 0;
foreach ($arr as $v) $sum += $v;
echo "foreach: " . (microtime(true) - $t) . "s\n";

// 2. for + count() を変数化
$t = microtime(true); $sum = 0; $n = count($arr);
for ($i = 0; $i < $n; $i++) $sum += $arr[$i];
echo "for: " . (microtime(true) - $t) . "s\n";

// 3. 組み込み関数 array_sum (圧倒的に速い)
$t = microtime(true);
$sum = array_sum($arr);
echo "array_sum: " . (microtime(true) - $t) . "s\n";

// 一般に: array_sum > foreach > for
// 組み込み関数があるなら最優先

FAQ

Q: foreach 中に配列要素を削除したい
A: unset($arr[$key]) は foreach 中でも安全(コピー走査)。ただし挙動が直感的でないケースもあるため、新配列に欲しい要素を集める方法を推奨。

Q: 配列が大きくて foreach が遅い
A: ジェネレータ (yield) でメモリ節約。あるいは組み込み関数 (array_map / array_filter) を活用。

Q: 連想配列の特定キーから走査開始したい
A: array_search でキー位置を得て array_slice、あるいは foreach + flag 変数で開始制御。