while 文 / do-while 文
繰り返し処理の中でも、繰り返し回数があらかじめ決まっていない場合に使用するのが『while』文と『do-while』文です。『PHP』ではこの2つの構文で、条件が満たされる間ループを継続する処理を記述できます。
構文
// while文:条件が真の間、ブロックを繰り返し実行します
while (条件式) {
// 条件式がtrueの間、繰り返し実行される処理
}
// do-while文:ブロックを必ず1回実行してから条件を評価します
do {
// 最低1回は必ず実行される処理
} while (条件式);
whileとdo-whileの違い
| 構文 | 条件の評価タイミング | 最低実行回数 |
|---|---|---|
| while | ブロック実行の前(前判定)です。 | 0回です。条件が最初から偽であれば一度も実行されません。 |
| do-while | ブロック実行の後(後判定)です。 | 1回です。条件に関わらず最初の1回は必ず実行されます。 |
最初から条件が『false』になる可能性がある場合は『while』を使用します。ループの本体を必ず1回実行してから継続の可否を判断したい場合は『do-while』を使用します。
ループ制御
| 構文 | 概要 |
|---|---|
| break | ループを即座に終了します。条件が成立した時点でそれ以降の繰り返しをすべてスキップします。 |
| break n | 数値を指定すると、ネストしたループをn段分まとめて抜けます。 |
| continue | 現在の繰り返しの残り処理をスキップして次の繰り返しへ進みます。条件式は再評価されます。 |
| continue n | 数値を指定すると、ネストしたループのn段分外側の次の繰り返しへ進みます。 |
サンプルコード
while_basic.php
<?php
// 呪術廻戦の術師が呪力を消費しながら術式を連続発動します
$characterName = "虎杖悠仁";
$cursedEnergy = 100; // 呪力の残量
// 呪力が20以上ある間、発動し続けます
while ($cursedEnergy >= 20) {
echo $characterName . ": 術式発動(残呪力: " . $cursedEnergy . ")\n";
$cursedEnergy -= 25; // 1回の発動で25消費します
}
echo $characterName . ": 呪力切れ(残呪力: " . $cursedEnergy . ")\n";
実行すると次のように出力されます。
php while_basic.php 虎杖悠仁: 術式発動(残呪力: 100) 虎杖悠仁: 術式発動(残呪力: 75) 虎杖悠仁: 術式発動(残呪力: 50) 虎杖悠仁: 術式発動(残呪力: 25) 虎杖悠仁: 呪力切れ(残呪力: 0)
do_while_basic.php
<?php
// do-while文:最初の1回は必ずメニューを表示し、選択結果によって継続します
// 条件が最初からfalseであっても最低1回は実行されます
$characterName = "五条悟";
$attemptCount = 0;
$maxAttempts = 3;
do {
$attemptCount++;
echo $characterName . ": 術式展開 " . $attemptCount . "回目\n";
} while ($attemptCount < $maxAttempts);
echo "合計 " . $attemptCount . " 回展開しました\n";
// 条件が最初からfalseでも1回は実行される例です
echo "\n--- 条件が最初からfalseの場合 ---\n";
$count = 10;
do {
echo "このブロックは必ず実行されます(count = " . $count . ")\n";
$count++;
} while ($count < 5); // 最初から false ですが、1回だけ実行されます
実行すると次のように出力されます。
php do_while_basic.php 五条悟: 術式展開 1回目 五条悟: 術式展開 2回目 五条悟: 術式展開 3回目 合計 3 回展開しました --- 条件が最初からfalseの場合 --- このブロックは必ず実行されます(count = 10)
while_file_read.php
<?php
// ファイル読み込みでのwhileの実用パターンです
// feof()がtrueになるまで1行ずつ読み込みます
$filename = "jujutsu_members.txt"; // ファイル名
$fp = fopen($filename, "r");
if ($fp === false) {
echo "ファイルを開けませんでした\n";
exit(1);
}
echo "--- 呪術師メンバー一覧 ---\n";
// ファイル終端に達するまで1行ずつ読み込みます
while (!feof($fp)) {
$line = fgets($fp);
if ($line !== false) {
echo trim($line) . "\n"; // 行末の改行を除去して出力します
}
}
fclose($fp); // 必ずファイルを閉じます
jujutsu_members.txt
虎杖悠仁 伏黒恵 釘崎野薔薇 五条悟
実行すると次のように出力されます。
php while_file_read.php --- 呪術師メンバー一覧 --- 虎杖悠仁 伏黒恵 釘崎野薔薇 五条悟
while_pdo_cursor.php
<?php
// PDOカーソル処理でのwhileの実用パターンです
// fetch()はレコードがある間は配列を、なくなるとfalseを返します
$dsn = "mysql:host=localhost;dbname=jujutsu;charset=utf8mb4";
$pdo = new PDO($dsn, "user", "password");
$stmt = $pdo->query("SELECT name, grade FROM sorcerers ORDER BY grade ASC");
echo "--- 呪術師一覧(等級順) ---\n";
// fetch()がfalseを返すまで繰り返し処理します
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['name'] . "(" . $row['grade'] . "等)\n";
}
実行すると次のように出力されます。
php while_pdo_cursor.php --- 呪術師一覧(等級順) --- 五条悟(特) 夏油傑(特) 七海建人(1) 伏黒甚爾(1) 虎杖悠仁(1)
do_while_retry.php
<?php
// do-whileはリトライ処理にも適しています
// 最初の試行が必ず1回実行されることを利用します
$characterName = "伏黒恵";
$maxRetries = 3;
$attempt = 0;
$success = false;
do {
$attempt++;
echo $characterName . ": 十種影法術 試行 " . $attempt . "回目\n";
// 3回目で成功するシミュレーションです
if ($attempt === 3) {
$success = true;
echo $characterName . ": 式神召喚に成功しました\n";
} else {
echo $characterName . ": 召喚失敗、再試行します\n";
}
} while (!$success && $attempt < $maxRetries);
if (!$success) {
echo $characterName . ": 最大試行回数に達しました\n";
}
実行すると次のように出力されます。
php do_while_retry.php 伏黒恵: 十種影法術 試行 1回目 伏黒恵: 召喚失敗、再試行します 伏黒恵: 十種影法術 試行 2回目 伏黒恵: 召喚失敗、再試行します 伏黒恵: 十種影法術 試行 3回目 伏黒恵: 式神召喚に成功しました
よくあるミス
よくあるミス1: 無限ループ(条件が常にtrue、またはループカウンタのインクリメント忘れ)
while文の条件式が常にtrueになるか、ループの中でカウンタや状態を更新し忘れると無限ループになります。特に「ループ内で条件に影響する変数を更新し忘れる」ミスが起きやすいです。
ng_while_infinite.php
<?php
// $cursedEnergy を減らし忘れているため条件が常に true になる
$characterName = "虎杖悠仁";
$cursedEnergy = 100;
while ($cursedEnergy >= 20) {
echo $characterName . ": 術式発動(残呪力: " . $cursedEnergy . ")\n";
// $cursedEnergy -= 25; が抜けているため無限ループになる
if ($cursedEnergy > 1000) break; // 緊急停止
}
実行すると次のように出力されます。
php ng_while_infinite.php 虎杖悠仁: 術式発動(残呪力: 100) 虎杖悠仁: 術式発動(残呪力: 100) 虎杖悠仁: 術式発動(残呪力: 100) ...(終わらない)
ループブロック内で条件に影響する変数を必ず更新します。
ok_while_infinite.php
<?php
$characterName = "虎杖悠仁";
$cursedEnergy = 100;
while ($cursedEnergy >= 20) {
echo $characterName . ": 術式発動(残呪力: " . $cursedEnergy . ")\n";
$cursedEnergy -= 25; // ループ内で必ず更新する
}
echo $characterName . ": 呪力切れ(残呪力: " . $cursedEnergy . ")\n";
実行すると次のように出力されます。
php ok_while_infinite.php 虎杖悠仁: 術式発動(残呪力: 100) 虎杖悠仁: 術式発動(残呪力: 75) 虎杖悠仁: 術式発動(残呪力: 50) 虎杖悠仁: 術式発動(残呪力: 25) 虎杖悠仁: 呪力切れ(残呪力: 0)
よくあるミス2: do-whileのセミコロン忘れ(Parse error)
do-while文の『while (条件式)』の後にはセミコロン(;)が必要です。for文やwhile文にはない規則のため、書き忘れることがあります。セミコロンが欠けているとParse errorが発生します。
ng_dowhile_semicolon.php
<?php
$characterName = "五条悟";
$count = 0;
do {
$count++;
echo $characterName . ": 術式展開 " . $count . "回目\n";
} while ($count < 3) // セミコロンが欠けている
実行すると次のように出力されます。
php ng_dowhile_semicolon.php Parse error: syntax error, unexpected end of file, expecting ";" in ...
do-while のwhile条件の末尾にセミコロンを付けます。
ok_dowhile_semicolon.php
<?php
$characterName = "五条悟";
$count = 0;
do {
$count++;
echo $characterName . ": 術式展開 " . $count . "回目\n";
} while ($count < 3); // セミコロンを忘れずに付ける
実行すると次のように出力されます。
php ok_dowhile_semicolon.php 五条悟: 術式展開 1回目 五条悟: 術式展開 2回目 五条悟: 術式展開 3回目
よくあるミス3: 条件式に副作用を持つ関数を使ったときの予期しない挙動
while文の条件式には任意の式を書けますが、副作用(DBアクセス・ファイル読み込みなど)を持つ関数を条件式に直接書くと、ループが終了する直前の最後のfetch/読み込みが無駄に実行されるケースがあります。また、条件式の評価回数が予想より多くなり、パフォーマンスに影響することもあります。
ng_while_side_effect.php
<?php
// fgets() は条件評価のたびに呼ばれる(ループ終了後にも1回余分に呼ばれる)
$fp = fopen("jujutsu_members.txt", "r");
while ($line = fgets($fp)) {
// $line が false(EOF)になったときにループが終わるが、
// fgets() 自体はループ終了の判定のためにEOFを読んだ時点で1回実行される
echo trim($line) . "\n";
}
fclose($fp);
実行すると次のように出力されます。
php ng_while_side_effect.php 虎杖悠仁 伏黒恵 釘崎野薔薇 五条悟
このパターン自体は動作しますが、fgets()がEOFを検知した時点で呼び出しが1回増えることを意識しておきます。ファイル全体を読む場合はfile()やfile_get_contents()を検討します。
ok_while_side_effect.php
<?php
// ファイル全体を配列で読み込む方法(fgets whileより行数が少ない)
$lines = file("jujutsu_members.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
echo $line . "\n";
}
実行すると次のように出力されます。
php ok_while_side_effect.php 虎杖悠仁 伏黒恵 釘崎野薔薇 五条悟
概要
『while』文は条件式を最初に評価する「前判定」ループです。条件が最初から『false』であればブロックは一度も実行されません。繰り返し回数が不定で、条件次第では実行されない可能性がある処理に適しています。
『do-while』文は条件式をブロック実行後に評価する「後判定」ループです。条件の値に関わらず最低1回は必ずブロックが実行されます。ユーザー入力の検証やリトライ処理など、「まず実行してから継続するかを判断する」場面に適しています。なお、do-while の閉じる『} while (条件式);』末尾のセミコロンを忘れやすいため注意が必要です。
ファイル読み込みでは『fopen() / fclose()』と組み合わせて『feof()』が『true』になるまでループするパターンが定番です。データベースのカーソル処理では PDO の『fetch()』が『false』を返すまでループするパターンが一般的です。PDO の使い方については『PDO(データベース接続)』も参照してください。繰り返し回数が明確な場合は『for文』のほうがコードの意図が明確になります。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。