はじめに
これまでの記事で、while(前判定)と do...while(後判定)という、条件式に基づく2種類のループ構文を解説してきました。今回取り上げる for は、それらとは異なり、「初期化」「継続条件」「更新処理」という3つの要素を1行にまとめて記述する、繰り返し回数が明確な処理に適したループ構文です。
for は、配列のインデックスを使った走査、固定回数の繰り返し処理、カウントダウンなど、「何回繰り返すか」があらかじめ分かっている、あるいは明確なルールで決まる処理において、while よりも簡潔かつ意図の伝わりやすいコードを書けます。本記事では基本的な使い方から、各セクションの省略・複数指定といった応用的な書き方、実践的な活用例まで詳しく解説します。
構文概要
| 項目 | 内容 |
|---|---|
| 構文名 | for |
| 種別 | 制御構造(ループ構文) |
| 基本構文 | for (初期化式; 条件式; 更新式) { 処理 } |
| 評価タイミング | 条件式はループの先頭で毎回評価される(前判定) |
| 各セクションの省略 | 初期化式・条件式・更新式はすべて省略可能 |
| 複数式の指定 | 各セクション内でカンマ区切りによる複数式の記述が可能 |
| 対応バージョン | PHP 4以前から存在する最古参の構文の一つ |
forの3つのセクション(イメージ図)
for ($i = 0; $i < 5; $i++) {
└─┬──┘ └─┬──┘ └┬─┘
初期化式 条件式 更新式
(最初に (毎回 (1回の繰り返しが
1回だけ実行) 評価) 終わるたびに実行)
echo $i;
}
│
▼
実行順序:
1. $i = 0 (初期化、1回のみ)
2. $i < 5 を評価 → trueなら3へ、falseなら終了
3. echo $i; (ループ本体の処理)
4. $i++ (更新)
5. 2に戻る
ポイントは、for がこの3つのセクションを1箇所にまとめて記述できることで、「どこからどこまで、どのように変化しながら繰り返すか」がループの定義部分だけを見て一目で分かるという点です。
実践サンプル7選
例1:基本的な使い方
<?php
for ($i = 0; $i < 5; $i++) {
echo "カウント: {$i}" . PHP_EOL;
}
例2:配列のインデックスを使った走査
<?php
$fruits = ['りんご', 'バナナ', 'オレンジ'];
/**
* インデックス番号そのものが必要な場合、
* foreachよりもforの方が自然に書けることがある
*/
for ($i = 0; $i < count($fruits); $i++) {
echo "{$i}番目: {$fruits[$i]}" . PHP_EOL;
}
例3:カウントダウン(逆順ループ)
<?php
for ($i = 10; $i > 0; $i--) {
echo $i . '... ';
}
echo '発射!' . PHP_EOL;
例4:複数の変数を同時に初期化・更新する
<?php
/**
* カンマ区切りで複数の式を指定することで、
* 2つの変数を同時に異なる方向へ動かすことができる
*/
for ($i = 0, $j = 10; $i < 5; $i++, $j--) {
echo "i={$i}, j={$j}" . PHP_EOL;
}
例5:ネストしたforによる2次元配列(テーブル)の生成
<?php
$rows = 3;
$cols = 3;
$table = [];
for ($row = 0; $row < $rows; $row++) {
for ($col = 0; $col < $cols; $col++) {
$table[$row][$col] = ($row * $cols) + $col + 1;
}
}
foreach ($table as $rowData) {
echo implode("\t", $rowData) . PHP_EOL;
}
例6:各セクションを省略した柔軟な書き方
<?php
/**
* forの各セクションは省略可能で、
* 条件式だけを使うとwhileとほぼ同じ挙動にできる
*/
$i = 0;
for (; $i < 3;) {
echo "柔軟な書き方: {$i}" . PHP_EOL;
$i++;
}
// さらにすべて省略し、breakで終了条件を制御する無限ループパターン
$count = 0;
for (;;) {
if ($count >= 2) {
break;
}
echo "無限ループ形式: {$count}" . PHP_EOL;
$count++;
}
例7:固定サイズのバッチ処理(チャンク分割)
<?php
function processBatch(array $items, int $batchSize): void
{
$total = count($items);
/**
* バッチサイズ単位でループを進める、典型的なバッチ処理パターン
*/
for ($offset = 0; $offset < $total; $offset += $batchSize) {
$batch = array_slice($items, $offset, $batchSize);
echo 'バッチ処理: ' . implode(', ', $batch) . PHP_EOL;
}
}
processBatch(range(1, 10), 3);
関連機能との比較
| 機能 | 役割 | forとの違い |
|---|---|---|
for | 初期化・条件・更新式をまとめた繰り返し | 本記事の対象。繰り返し回数が明確な処理に適する |
while | 条件がtrueの間、処理を繰り返す(前判定) | 初期化や更新処理を自分でループ外・内に書く必要がある |
do...while | 条件がtrueの間、処理を繰り返す(後判定) | 最低1回の実行が保証される点がforとは異なる |
foreach | 配列やイテレータの要素を順に処理する | インデックス管理が不要で、配列の走査に特化している |
array_slice() | 配列の一部を抽出する関数 | ループ構文ではなく、forと組み合わせてバッチ処理を実現する際によく使われる |
よくある落とし穴(注意点)
- ループ内で
count()を毎回呼び出すパフォーマンスの問題for ($i = 0; $i < count($array); $i++)のように書くと、ループのたびにcount()が呼び出され、大きな配列では無視できないオーバーヘッドになることがあります。ループ開始前に$length = count($array);として変数に保持し、それを条件式で使う方が効率的です(例2の書き方は小規模な例のため問題ありませんが、大きな配列では注意が必要です)。 - ループ変数のスコープがループの外にも残る
for ($i = 0; ...)で宣言した$iは、ループを抜けた後もその変数のスコープ内で参照可能です。複数のループで同じ変数名($iなど)を使い回すと、意図しない値の混在を招くことがあります。 - 無限ループに陥りやすい条件・更新式の誤り 条件式や更新式を誤って記述すると(例:
$i++を書き忘れる、条件の不等号を間違えるなど)、意図せず無限ループになってしまいます。特にfor (;;) { ... break; }のような形式でbreakの条件を誤ると、永遠に終了しないループになります(例6を参照)。 - 各セクションの省略は可読性とのトレードオフ 初期化式・条件式・更新式を省略する書き方(例6)は柔軟ですが、
for本来の「3要素が揃っている」という分かりやすさを損なうことがあります。省略が必要な場面は限定的であり、通常は3要素をすべて明記した方が読みやすいコードになります。 - 配列の単純な走査には
foreachの方が適している場合が多い インデックス番号自体が処理に必要ない場合、forよりもforeachを使う方が、コードが簡潔になり、インデックスの範囲外アクセスといったバグのリスクも減らせます。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする構文か | 初期化式・条件式・更新式を1つにまとめ、条件がtrueの間処理を繰り返す |
| 主な用途 | 配列のインデックス走査、固定回数の繰り返し、カウントダウン、バッチ処理 |
| 3つのセクション | 初期化(最初の1回)、条件(毎回評価)、更新(毎回の繰り返し後) |
whileとの違い | 繰り返しに関わる3要素を1箇所にまとめて記述できる |
| 注意点 | count()の毎回呼び出しによるパフォーマンス低下、無限ループのリスク、foreachの方が適する場面 |
for は、繰り返し回数やインデックスの変化パターンが明確な処理において、その意図をコンパクトに表現できる、PHPの基本的なループ構文です。配列の単純な走査には foreach を、終了条件が不確定な処理には while を使うなど、それぞれのループ構文の特性を理解した上で、最適なものを選択することが重要です。
