[PHP]switch文の緩やかな比較についての検証
著:taiki_kimura
はじめに
案件でswitch文の緩やかな比較について考える機会があったので投稿します。
PHPのswitch文で比較演算子を使用した場合の挙動について検証してみました。
内容
まずはPHPのswitch文のリファレンスを確認します。
PHP: switch - Manual
phpのswitch文の条件判定では、緩やかな比較によって処理されます。
緩やかな比較とは、==
によって行われるため、型が異なる同士の比較は自動的に型変換が行われます。(緩やかな比較と厳密な比較の詳細については割愛します)
この緩やかな比較は注意が必要なため、リファレンスにも割とあっさりめですが書かれています。
注意:
switch/case が行うのは、 緩やかな比較 であることに注意しましょう。
肝心な緩やかな比較についての表は下記にまとめられています。
PHP: PHP 型の比較表 - Manual
比較演算子については下記の表に沿って上から順に比較されていくことも要確認です。
PHP: 比較演算子 - Manual
検証
では検証していきます。
検証用の値を配列で用意し、switch文で条件分岐させます。記事の便宜上、0〜6番のkeyを設定しています。また、foreach文は表示のために記述しているのでスルーして下さい。
// 検証用の値
$calc_list = [
0 => -1,
1 => 0,
2 => 1,
3 => 5,
4 => false,
5 => true,
6 => null
];
foreach ($calc_list as $key => $calc) {
echo $key . '番目:';
// switch文は緩やかな比較
switch ($calc) {
case $calc < 5:
echo '5未満です' . "\n";
break;
case 5 < $calc:
echo '6以上です' . "\n";
break;
default:
echo 'defultです' . "\n";
break;
}
}
実行結果が以下の通りです。各行の左の値は配列の番号を示しています。
0番目:5未満です
1番目:6以上です
2番目:5未満です
3番目:defultです
4番目:6以上です
5番目:defultです
6番目:6以上です
解説
配列のキーの番号ごとに出力された結果について解説していきます。
0番目、2番目、3番目
最初に、0番目
、2番目
、3番目
の配列の値ですが、caseの比較演算子の通り、表示されているかと思います。(表現の仕方について全ての結果は比較演算子の通りで間違いないのですが、分かりやすいという意味で使ってます)
0番目
は、-1
のため、5未満です
が表示。
2番目
は 、1
のため、5未満です
が表示。
3番目
は 、5
のため、一致する条件が無くdefaultです
が表示。
1番目: 0
では、1番目
の配列についてです。
値に0
が入っていますが、判定としては6以上です
となっています。
ここが緩やかな比較の注意する点となります。
答えとしては、$calc
が0
の場合、case 5 < $calc
がfalse
のため、0 == false
により条件が一致することにより、判定として6以上です
が表示されます。
最初の比較式のcase $calc < 5
ではtrue
となり、条件が一致しないため次の比較が処理されます。
せっかくなので、緩やかな比較の対応表から見た一致する値です。
<img title='PHP__PHP_型の比較表_-Manual-2.png' alt='PHP__PHP_型の比較表-_Manual-2' src='/attachments/3a0977b2-df33-42a0-a13a-368e118c56be' width="1128" data-meta='{"width":1128,"height":826}'>
4番目: false
値がfalse
の場合は、数値と比較する際にbool変換して判定を行っています。
リファレンスに記載されている両辺を bool に変換し、false < true と判断しますの判定となります。
bool型の比較例
var_dump(100 < TRUE); // FALSE - same as (bool)100 < TRUE
var_dump(-10 < FALSE);// FALSE - same as (bool)-10 < FALSE
本題の式でも確認してみました。
$cal = false;
var_dump($cal < 5);
var_dump(5 < $cal);
bool(true)
bool(false)
よって、5 < $calc
がfalse
となり、false == false
により条件が一致するため6以上です
が表示されます。
5番目: true
こちらも4番目の配列と同様にvar_dumpを使用して結果を確認してみました。
$cal = true;
var_dump($cal < 5);
var_dump(5 < $cal);
bool(false)
bool(false)
結果から一致するcaseが無いためdefault文の処理defultです
が実行されます。
6番目: null
値がnull
の場合も同様にvar_dumpを使用して結果を確認してみました。
$cal = null;
var_dump($cal < 5);
var_dump(5 < $cal);
bool(true)
bool(false)
$cal < 5
ではtrue
となりますが、null
は型変換によりfalse
となるため一致しません。
5 < $calc
ではfalse
となり、条件が一致するため、6以上です
が表示されます。
番外編: 文字列
PHP8から数値と文字列を緩やかな比較で行った場合、結果がfalseに仕様変更となりました。
PHP7まで:0 == "php"
はtrue
PHP8 :0 == "php"
はfalse
参考:PHP 型の比較表
- PHP 8.0.0 より前のバージョンでは、true でした。
検証用の式でもPHPの上記のバージョンによって結果が異なります。
最新のPHP8を使用する場合はこのような仕様変更も注意が必要ですね。
おわりに
条件分岐においてswitch文を使用する機会はあると思いますが、比較する際は緩やかな比較であるということに注意が必要です。
本来、今回の検証の式のような比較を伴う厳密な条件分岐においては、switch文は適さないと思うため素直に別の方法(if文など)を考えたほうが良いと思います。
switch(true)による厳密比較の方法もありますが、if文の方が本来の書き方からして合ってそうな感じがします。
検証に当たってPHPのソースコードを覗いて見ましたが、こちらをベースに解説するには骨が折れるため挫折しました 興味があったら読んでみて下さい。
ソースコードを追った記事もありますので参考までに。
PHPの緩やかな比較の実態 - Qiita
宣伝
パーソンリンクではエンジニアを募集しています!
Discussion