switch 文
複数の値に応じて処理を切り替えるには、条件分岐の一種である『switch』文が使えます。『PHP』では『switch』文を使って、ひとつの変数や式を複数の値と照合し、一致したブロックを実行できます。
構文
switch (式) {
case 値1:
// 式が値1と一致した場合に実行される処理
break;
case 値2:
// 式が値2と一致した場合に実行される処理
break;
default:
// どのcaseにも一致しなかった場合に実行される処理
break;
}
構文の各部分
| 構文 | 概要 |
|---|---|
| switch (式) | 照合する値を指定します。変数、関数の戻り値、計算式など任意の式を記述できます。 |
| case 値: | 照合する値を指定します。『switch』の式と一致した場合、そのブロックの処理が実行されます。 |
| break | 現在の『case』ブロックを抜けて、『switch』全体の処理を終了します。省略するとフォールスルーが発生します。 |
| default: | 省略可能です。どの『case』にも一致しなかった場合に実行されるブロックです。 |
switch文とif文の使い分け
| 構文 | 適しているケース |
|---|---|
| switch | ひとつの変数を複数の固定値と照合する場合に使用します。caseの数が多いほどif文より見やすくなります。 |
| if / elseif | 範囲の比較(>=, <= など)や複数の変数を組み合わせた条件に使用します。 |
比較の仕組み(ゆるい比較)
『switch』文の比較は『==』(ゆるい比較)で行われます。型変換が発生するため、文字列と整数を混在させると意図しない結果になることがあります。
| switchの式 | caseの値 | 一致するか |
|---|---|---|
| 0 | "foo"(文字列) | 一致します("foo" が整数に変換されると 0 になるため)。 |
| 0 | ""(空文字列) | 一致します(空文字列が整数に変換されると 0 になるため)。 |
| 0 | false | 一致します(false が整数に変換されると 0 になるため)。 |
| "1" | 1 | 一致します(型変換が行われるため)。 |
| null | 0 | 一致します(null が整数に変換されると 0 になるため)。 |
型変換による意図しない一致を防ぐには、PHP 8以降では厳密比較を行う『match』式も利用できます。
フォールスルー
『break』を省略すると、一致した『case』の処理が終わった後も次の『case』の処理が続けて実行されます。これを「フォールスルー」と呼びます。
| パターン | 概要 |
|---|---|
| breakあり | 一致した『case』のブロックだけ実行され、『switch』全体を抜けます。通常はこちらを使用します。 |
| breakなし(フォールスルー) | 一致した『case』から始まり、次の『break』または『switch』の末尾まで処理が続きます。複数の値に同じ処理を適用したい場合に意図的に使用することがあります。 |
match式との違い(PHP 8以降)
| 項目 | switch文 | match式 |
|---|---|---|
| 比較方法 | ゆるい比較(==)を使用します。型変換が発生します。 | 厳密比較(===)を使用します。型変換は発生しません。 |
| フォールスルー | breakを省略すると発生します。 | 発生しません。各アームは独立しています。 |
| 戻り値 | 文を実行するだけで値を返しません。 | 式として値を返すため変数への代入が可能です。 |
| 一致しない場合 | defaultがなければ何も実行されません。 | defaultがなく一致しない場合は例外が発生します。 |
| 複数条件 | 複数のcaseを並べてフォールスルーで対応します。 | カンマ区切りで複数の値をひとつのアームにまとめられます。 |
PHP 8以降では型安全な『match』式も利用できます。既存のコードとの互換性や、PHPバージョンの制約がある場合は『switch』文が選択肢になります。
サンプルコード
switch_basic.php
<?php
// PSYCHO-PASSの執行官のドミネーターモードを判定します
$characterName = "狡噛慎也";
$crimeCoefficient = 180; // 犯罪係数
// 犯罪係数に基づいてモードを決定します
if ($crimeCoefficient < 80) {
$mode = "non-lethal";
} elseif ($crimeCoefficient < 300) {
$mode = "paralyzer";
} else {
$mode = "eliminator";
}
// switch文でモードに応じたメッセージを切り替えます
switch ($mode) {
case "non-lethal":
echo $characterName . ": ドミネーター使用不可(係数: " . $crimeCoefficient . ")\n";
break;
case "paralyzer":
echo $characterName . ": 非致死性麻痺モード(係数: " . $crimeCoefficient . ")\n";
break;
case "eliminator":
echo $characterName . ": 致死性排除モード(係数: " . $crimeCoefficient . ")\n";
break;
default:
echo $characterName . ": 判定不能\n";
break;
}
実行すると次のように出力されます。
php switch_basic.php 狡噛慎也: 非致死性麻痺モード(係数: 180)
switch_fallthrough.php
<?php
// フォールスルーを意図的に使って複数の値に同じ処理を適用します
// PSYCHO-PASSの部署に応じて権限レベルを割り当てます
$division = "一係";
switch ($division) {
case "一係":
case "二係":
case "三係":
// 一係・二係・三係はすべて同じ処理になるため、breakなしでフォールスルーします
$accessLevel = "現場執行権限";
break;
case "分析部門":
$accessLevel = "情報閲覧権限";
break;
default:
$accessLevel = "基本権限";
break;
}
echo $division . ": " . $accessLevel . "\n";
実行すると次のように出力されます。
php switch_fallthrough.php 一係: 現場執行権限
switch_type_pitfall.php
<?php
// switchのゆるい比較による意図しない一致の例です
// 犯罪係数をフォームから受け取った場合、文字列になることがあります
$coefficient = 0; // 整数の0
echo "--- ゆるい比較(switch)の落とし穴 ---\n";
// 整数の0はゆるい比較でfalseや"unset"(非数値文字列)と一致してしまいます
switch ($coefficient) {
case false:
// 0 == false はtrueのため、こちらが実行されます
echo "falseと一致しました(意図しない一致)\n";
break;
case 0:
echo "整数の0と一致しました\n";
break;
}
// 文字列の犯罪係数と整数のcaseを混在させた場合の注意点です
$rawInput = "150"; // フォームからの入力(文字列)
switch ($rawInput) {
case 150:
// "150" == 150 はtrueのため、こちらが実行されます(型変換が発生)
echo "整数150と一致しました(型変換が発生)\n";
break;
case "150":
echo "文字列「150」と一致しました\n";
break;
}
実行すると次のように出力されます。
php switch_type_pitfall.php --- ゆるい比較(switch)の落とし穴 --- falseと一致しました(意図しない一致) 整数150と一致しました(型変換が発生)
switch_vs_match.php
<?php
// switch文とmatch式(PHP 8以降)の違いを比較します
// PSYCHO-PASSのキャラクターの役職を判定します
$characterName = "常守朱";
$rank = "監視官";
// switch文による判定(ゆるい比較)
echo "--- switch文 ---\n";
switch ($rank) {
case "監視官":
echo $characterName . ": 公安局刑事課の監視官です\n";
break;
case "執行官":
echo $characterName . ": 潜在犯として登録されています\n";
break;
default:
echo $characterName . ": 一般市民です\n";
break;
}
// match式による判定(厳密比較・PHP 8以降)
// 戻り値を変数に代入できるため、簡潔に記述できます
echo "--- match式(PHP 8以降) ---\n";
$description = match($rank) {
"監視官" => "公安局刑事課の監視官です",
"執行官" => "潜在犯として登録されています",
default => "一般市民です",
};
echo $characterName . ": " . $description . "\n";
// matchは厳密比較のため、型が異なると一致しません
$numericRank = 1; // 数値で渡した場合
$result = match($numericRank) {
1 => "監視官(整数1)",
default => "一致なし",
};
echo "数値1のmatch結果: " . $result . "\n";
実行すると次のように出力されます。
php switch_vs_match.php --- switch文 --- 常守朱: 公安局刑事課の監視官です --- match式(PHP 8以降) --- 常守朱: 公安局刑事課の監視官です 数値1のmatch結果: 監視官(整数1)
switch_in_function.php
<?php
// 関数の中でswitch文を使い、returnで値を返す実用パターンです
// PSYCHO-PASSのキャラクターの犯罪係数から色相を判定します
function getHueStatus(int $coefficient): string
{
// 係数の範囲を判定してからswitch文で文字列を切り替えます
if ($coefficient < 0) {
return "計測不能";
}
// 範囲をカテゴリに変換してからswitch文で処理します
if ($coefficient < 80) {
$category = "clear";
} elseif ($coefficient < 100) {
$category = "cloudy";
} elseif ($coefficient < 300) {
$category = "dominant";
} else {
$category = "critical";
}
switch ($category) {
case "clear":
return "クリア(" . $coefficient . "): 問題なし";
case "cloudy":
return "曇り(" . $coefficient . "): 要注意";
case "dominant":
return "支配的(" . $coefficient . "): 執行対象";
case "critical":
return "危機的(" . $coefficient . "): 即時排除対象";
default:
return "不明";
}
}
$characters = [
["name" => "常守朱", "coefficient" => 28],
["name" => "宜野座伸元", "coefficient" => 89],
["name" => "狡噛慎也", "coefficient" => 148],
["name" => "槙島聖護", "coefficient" => 380],
];
foreach ($characters as $character) {
echo $character['name'] . ": " . getHueStatus($character['coefficient']) . "\n";
}
実行すると次のように出力されます。
php switch_in_function.php 常守朱: クリア(28): 問題なし 宜野座伸元: 曇り(89): 要注意 狡噛慎也: 支配的(148): 執行対象 槙島聖護: 危機的(380): 即時排除対象
よくあるミス
よくあるミス1: break忘れによるフォールスルー(意図しない複数caseの実行)
『break』を書き忘れると、一致した『case』の処理が終わった後も次の『case』の処理が続けて実行されます。これを「フォールスルー」と呼びます。意図しない場合は複数の処理が実行されてしまいます。
ng_break.php
<?php
// PSYCHO-PASSのキャラクターの権限レベルを判定します
$rank = "監視官";
switch ($rank) {
case "監視官":
echo "監視官権限が付与されました\n";
// breakを書き忘れています — 次のcaseに処理が流れます
case "執行官":
echo "執行官権限が付与されました\n";
break;
default:
echo "一般権限のみです\n";
break;
}
実行すると次のように出力されます。
php ng_break.php 監視官権限が付与されました 執行官権限が付与されました
ok_break.php
<?php
$rank = "監視官";
switch ($rank) {
case "監視官":
echo "監視官権限が付与されました\n";
break; // breakを忘れずに書きます
case "執行官":
echo "執行官権限が付与されました\n";
break;
default:
echo "一般権限のみです\n";
break;
}
実行すると次のように出力されます。
php ok_break.php 監視官権限が付与されました
よくあるミス2: ゆるい比較による型変換の罠(0 == "abc" がtrue)
『switch』文は『==』(ゆるい比較)で照合するため、PHP 7 では整数の『0』と文字列を比較すると文字列が整数に変換されて『0』になり、一致してしまいます。PHP 8 ではこの挙動が変更され、整数と非数値文字列の比較では文字列が整数に変換されなくなったため、『0 == "unset"』は『false』になります。フォームやAPIから受け取った文字列を整数の『case』と照合する場合に注意が必要です。
ng_loose_comparison.php
<?php
// 犯罪係数が数値0(計測不能)かどうかを判定します
$coefficient = 0;
switch ($coefficient) {
case "unset": // 文字列"unset"
// PHP 7 では 0 == "unset" がtrueになります("unset"が整数に変換されると0になるため)
// PHP 8 では 0 == "unset" がfalseになり、このcaseにはマッチしません
echo "係数未設定\n";
break;
case 0:
echo "係数ゼロ(計測不能)\n";
break;
}
実行すると次のように出力されます。
php ng_loose_comparison.php 係数ゼロ(計測不能)
PHP 7 では 0 == "unset" が true と評価されるため case "unset" にマッチしていましたが、PHP 8 では false となり case "unset" はスキップされ、続く case 0 にマッチします。
ok_loose_comparison.php
<?php
$coefficient = 0;
// match式(PHP 8以降)を使うと厳密比較になります
$status = match($coefficient) {
"unset" => "係数未設定", // 厳密比較のため 0 === "unset" はfalse
0 => "係数ゼロ(計測不能)",
default => "係数あり",
};
echo $status . "\n";
実行すると次のように出力されます。
php ok_loose_comparison.php 係数ゼロ(計測不能)
よくあるミス3: defaultの位置とbreak忘れの組み合わせで混乱する
『default』は必ずしも最後でなくても動作しますが、途中に配置するとフォールスルーと組み合わさって予期しない動作になることがあります。『default』は末尾に書くのが一般的です。
ng_default_position.php
<?php
$rank = "一般市民";
// defaultを途中に置き、breakを書き忘れた場合の動作です
switch ($rank) {
case "監視官":
echo "監視官です\n";
break;
default:
// 一致するcaseがないためdefaultが実行され、breakがないため次のcaseに流れます
echo "その他の権限者です\n";
// breakがないため次のcase("執行官")に処理が流れます
case "執行官":
echo "執行官の処理も実行されました\n";
break;
}
実行すると次のように出力されます。
php ng_default_position.php その他の権限者です 執行官の処理も実行されました
ok_default_position.php
<?php
$rank = "一般市民";
// defaultは末尾に配置し、breakを忘れずに書きます
switch ($rank) {
case "監視官":
echo "監視官です\n";
break;
case "執行官":
echo "執行官です\n";
break;
default:
echo "その他の権限者です\n";
break;
}
実行すると次のように出力されます。
php ok_default_position.php その他の権限者です
概要
『switch』文はひとつの式を複数の固定値と順番に照合し、一致した『case』ブロックを実行する条件分岐構文です。比較には『==』(ゆるい比較)が使われるため、整数の『0』が文字列やfalseと一致するなど、型変換による意図しない結果が発生することがあります。型変換の仕組みについては『型キャスト』を参照してください。
各『case』ブロックの末尾には原則として『break』を記述します。『break』を省略すると「フォールスルー」が発生し、次の『case』のブロックがそのまま実行されます。フォールスルーは複数の値に同じ処理を適用したい場合に意図的に使用することがありますが、うっかり『break』を忘れると想定外の処理が実行される点に注意が必要です。
PHP 8以降では『match』式が利用できます。『match』式は厳密比較(===)で照合するためゆるい比較の落とし穴がなく、フォールスルーも発生しません。また、式として値を返せるため変数への代入が可能です。新しいコードを書く場合はPHPバージョンを確認のうえ、『match』式が使える環境であれば選択肢に入ります。条件が範囲の比較になる場合や複数の変数を組み合わせる場合は、『if / elseif / else』が適しています。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。