はじめに
前回の記事では、ループや switch を即座に終了させる break を解説し、その対として度々登場していたのが今回取り上げる continue です。
continue は、break のようにループ全体を終了させるのではなく、現在の繰り返し(イテレーション)だけをその場でスキップし、次の繰り返しに進むための制御構文です。「特定の条件に合致する要素だけは処理をスキップしたいが、ループ自体は最後まで続けたい」という場面で活躍します。break と並んで break n; のような数値引数による階層指定も可能で、ネストしたループの制御にも使われます。本記事では基本的な使い方から、break との違い、実践的な活用例まで詳しく解説します。
構文概要
| 項目 | 内容 |
|---|---|
| 構文名 | continue |
| 種別 | 制御構造(言語構文) |
| 基本構文 | continue; または continue n;(nは対象とするループの階層数) |
| 対象 | while、do-while、for、foreach |
| 使用場所 | ループのブロック内でのみ有効 |
switch内での注意点 | switch内のcontinueはbreakと同じ意味になる(後述) |
| 対応バージョン | PHP 4以前から存在する最古参の構文の一つ |
continueの動作(イメージ図)
for ($i = 0; $i < 5; $i++) {
if ($i === 2) {
continue; ← この回だけスキップし、$i++へジャンプする
}
echo $i;
}
│
▼
出力: 0, 1, 3, 4
($i === 2 の時だけechoがスキップされるが、
ループ自体は最後まで継続される)
ポイントは、continue がループを終了させるのではなく、その回の残りの処理だけを飛ばして、次の繰り返しへ進むという点です。for ループの場合、continue が実行されると、ブロック内の残りの処理はスキップされますが、更新式($i++など)は通常通り実行されてからループが継続します。
実践サンプル7選
例1:基本的な使い方
<?php
for ($i = 1; $i <= 10; $i++) {
if ($i % 2 !== 0) {
continue; // 奇数はスキップする
}
echo "偶数: {$i}" . PHP_EOL;
}
例2:配列をフィルタリングしながら処理する
<?php
$users = [
['name' => '太郎', 'active' => true],
['name' => '花子', 'active' => false],
['name' => '次郎', 'active' => true],
];
foreach ($users as $user) {
if (!$user['active']) {
continue; // 非アクティブなユーザーは処理対象から除外する
}
echo "{$user['name']}さんにメールを送信します" . PHP_EOL;
}
例3:continueを使った早期スキップによるネストの回避
<?php
function processOrder(array $order): void
{
/**
* else句で処理を囲む代わりに、
* 条件に合わなければ即座にcontinueする(ガード節的な活用)
*/
foreach ($order['items'] as $item) {
if ($item['quantity'] <= 0) {
echo "{$item['name']}: 数量が0以下のためスキップ" . PHP_EOL;
continue;
}
if ($item['price'] < 0) {
echo "{$item['name']}: 価格が不正のためスキップ" . PHP_EOL;
continue;
}
$subtotal = $item['quantity'] * $item['price'];
echo "{$item['name']}: 小計 {$subtotal}円" . PHP_EOL;
}
}
processOrder(['items' => [
['name' => 'ペン', 'quantity' => 3, 'price' => 100],
['name' => 'ノート', 'quantity' => 0, 'price' => 200],
]]);
例4:ネストしたループでの数値引数による階層指定
<?php
$matrix = [[1, -2, 3], [4, 5, -6], [7, 8, 9]];
foreach ($matrix as $rowIndex => $row) {
foreach ($row as $value) {
if ($value < 0) {
// continue 2 とすることで、内側のループだけでなく
// 外側のforeachの次の繰り返しへ直接進む
echo "行{$rowIndex}に負の値があるためスキップします" . PHP_EOL;
continue 2;
}
echo "処理中: {$value}" . PHP_EOL;
}
}
例5:whileループでのcontinue使用時の注意点
<?php
$i = 0;
while ($i < 5) {
$i++; // continueの前に更新処理を書く必要がある
if ($i === 3) {
continue; // ここでスキップしても、$i++は既に実行済み
}
echo "値: {$i}" . PHP_EOL;
}
例6:switch内でのcontinueの落とし穴(breakとの違い)
<?php
foreach ([1, 2, 3] as $i) {
switch ($i) {
case 2:
/**
* switch内でcontinueを使うと、PHPでは
* break と同じ意味になる(switchを抜けるだけ)
* ループの次の繰り返しへスキップしたい場合は
* continue 2 と明示的に書く必要がある
*/
continue; // これはswitchを抜けるだけで、foreachは継続する
default:
echo "処理: {$i}" . PHP_EOL;
}
}
例7:CSVデータのヘッダー行をスキップして処理する
<?php
function processCsvLines(array $lines): array
{
$results = [];
foreach ($lines as $index => $line) {
if ($index === 0) {
continue; // 1行目(ヘッダー)は処理対象から除外する
}
$results[] = str_getcsv($line);
}
return $results;
}
print_r(processCsvLines(['name,age', '太郎,30', '花子,25']));
関連機能との比較
| 機能 | 役割 | continueとの違い |
|---|---|---|
continue | 現在の繰り返しをスキップし、次の繰り返しに進む | 本記事の対象。ループ自体は終了させない |
break | ループやswitchを即座に抜ける | ループ全体を終了させる点が、continueと大きく異なる(前回記事を参照) |
return(関数内) | 関数の実行を終了し、値を返す | ループを含む関数全体の処理を終える、より強い制御構文 |
| ガード節(早期リターン) | 条件に合致しない場合に処理を早期に終える設計パターン | 関数全体のレベルでの類似した発想。continueはループ内限定 |
| 条件式での除外 | if文で処理対象を絞り込む | continueを使わず、処理内容全体をifブロックで包む代替アプローチ |
よくある落とし穴(注意点)
switch内のcontinueはbreakと同じ意味になる これがcontinueに関する最も有名で紛らわしい落とし穴です。switchがループの中にネストしている場合、switch内で単にcontinue;と書いても、それはswitchを抜けるだけ(break;と同じ効果)であり、外側のループの次の繰り返しへは進みません。意図した通りにループの繰り返しをスキップしたい場合は、continue 2;のように明示的に階層を指定する必要があります(例6を参照)。PHPはこの挙動について、バージョンによっては警告(Warning: "continue" targeting switch)を出すこともあります。forループでの更新式のタイミングを理解するforループ内でcontinueを使うと、ブロック内の残りの処理はスキップされますが、forの更新式($i++など)は通常通り実行されてからループが継続します。whileループでは、更新処理をループ内のどこに書くかによって、continue実行前にその処理が済んでいるかどうかが変わるため注意が必要です(例5を参照)。- 数値引数の階層の数え方は
breakと同様continue n;のnは、break n;と同じく「内側から数えて何階層分の繰り返しをスキップするか」を表します。深いネストでは、どの階層に影響するのかが分かりにくくなるため、過度な多用は避けましょう(例4を参照)。 continueの乱用はロジックを追いにくくする 1つのループ内にcontinueが何度も登場すると、「結局このループは何を処理しているのか」が読み取りにくくなることがあります。各continueの条件が独立した意味を持つ場合は問題ありませんが、複雑な条件分岐が絡む場合は、処理そのものを関数に切り出すことも検討しましょう(例3を参照)。continueと単純なifブロックのどちらが読みやすいかを検討する 「特定の条件の時だけ処理する」という単純なケースでは、continueでスキップするのではなく、処理全体をifブロックで囲む方が分かりやすい場合もあります。状況に応じて、どちらがより自然に意図を表現できるかを考えましょう。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする構文か | 現在のループの繰り返しをその場でスキップし、次の繰り返しへ進む |
| 主な用途 | 条件に合致しない要素の除外、データのフィルタリング処理、ヘッダー行のスキップ |
| 数値引数 | continue n;でネストしたループの、より外側の繰り返しをスキップできる |
breakとの違い | continueはループを継続、breakはループ全体を終了する |
| 最重要の注意点 | switchがループ内にある場合、単純なcontinueはbreakと同じ意味になること |
continue は、break と対をなす基本的なループ制御構文であり、特定の要素だけを処理対象から除外しながらループを継続したい場面で重宝します。switch との組み合わせにおける紛らわしい挙動にさえ注意すれば、データのフィルタリングや前処理のスキップなど、日常的なコーディングで安全に活用できる構文です。
