PHPの==がキモい件
どうやら僕はround()の人なんだそうです(参考資料)。それはそうとして、もう少し前までは==の人だった気がしますので、その頃の話題を再掲します。
僕は2年ほど前に「==がキモい件」などのタイトルで勉強会で発表していた頃がありました。PHPの==演算子の挙動について、啓蒙が必要だろうと考えていたためです。当時に比べれば最近はPHPの==演算子の不思議な挙動に関する記事を見る気がしますが、まだまだキモさの全容を把握している人は少ないような気もします。
PHPの==演算子の何がキモいのか、一言で言うと両辺の値の型をテキトーに合わせて比較する点です。言い換えると、「型はさておき、等しい値を意味しているようならtrue」という演算子です。
マニュアルの「PHP 型の比較表」の「==による緩やかな比較」を見てもキモさがチラホラ見えるんですが、今回は更に突っ込んだ比較表を作ってみました。
まずは以前の勉強会で紹介した表のダイジェスト版を紹介します。主にfalseっぽい値の比較表です。推移律(a==bかつb==cならa==c)が成り立たない場所が何カ所か存在します。
array(0) { } | NULL | bool(false) | int(0) | string(0) "" | string(1) "0" | string(2) "00" | string(3) "0.0" | string(1) " " | string(1) "" | bool(true) | |
---|---|---|---|---|---|---|---|---|---|---|---|
array(0) { } | true | true | true | false | false | false | false | false | false | false | false |
NULL | true | true | true | true | true | false | false | false | false | false | false |
bool(false) | true | true | true | true | true | true | false | false | false | false | false |
int(0) | false | true | true | true | true | true | true | true | true | true | false |
string(0) "" | false | true | true | true | true | false | false | false | false | false | false |
string(1) "0" | false | false | true | true | false | true | true | true | false | false | false |
string(2) "00" | false | false | false | true | false | true | true | true | false | false | true |
string(3) "0.0" | false | false | false | true | false | true | true | true | false | false | true |
string(1) " " (半角スペース1文字) | false | false | false | true | false | false | false | false | true | false | true |
string(1) "" (ヌル文字1文字、0x00) | false | false | false | true | false | false | false | false | false | true | true |
bool(true) | false | false | false | false | false | false | true | true | true | true | true |
次に浮動小数点数を比較してみました。INFやNANが怪しい気もしますが、他は普通です。
float(0) | float(1.0E+300) | float(1.0E-300) | float(INF) | float(-INF) | float(NAN) | |
---|---|---|---|---|---|---|
float(0) | true | false | false | false | false | false |
float(1.0E+300) | false | true | false | false | false | false |
float(1.0E-300) | false | false | true | false | false | false |
float(INF) | false | false | false | false | false | false |
float(-INF) | false | false | false | false | false | false |
float(NAN) | false | false | false | false | false | false |
次は0っぽい値の比較表です。表を作る意味がないですね。
int(0) | float(0) | string(1) "0" | string(2) "00" | string(3) "0.0" | string(3) "0x0" | string(3) "0X0" | string(3) "0e1" | string(3) "0E1" | string(7) "1e-1000" | |
---|---|---|---|---|---|---|---|---|---|---|
int(0) | true | true | true | true | true | true | true | true | true | true |
float(0) | true | true | true | true | true | true | true | true | true | true |
string(1) "0" | true | true | true | true | true | true | true | true | true | true |
string(2) "00" | true | true | true | true | true | true | true | true | true | true |
string(3) "0.0" | true | true | true | true | true | true | true | true | true | true |
string(3) "0x0" | true | true | true | true | true | true | true | true | true | true |
string(3) "0X0" | true | true | true | true | true | true | true | true | true | true |
string(3) "0e1" | true | true | true | true | true | true | true | true | true | true |
string(3) "0E1" | true | true | true | true | true | true | true | true | true | true |
string(7) "1e-1000" | true | true | true | true | true | true | true | true | true | true |
次は1っぽい値の比較表です。これまた無意味な表です。
int(1) | float(1) | string(1) "1" | string(2) "01" | string(3) "1.0" | string(3) "0x1" | string(3) "0X1" | string(3) "1e0" | string(3) "1E0" | string(5) "10e-1" | string(6) "0.1e+1" | |
---|---|---|---|---|---|---|---|---|---|---|---|
int(1) | true | true | true | true | true | true | true | true | true | true | true |
float(1) | true | true | true | true | true | true | true | true | true | true | true |
string(1) "1" | true | true | true | true | true | true | true | true | true | true | true |
string(2) "01" | true | true | true | true | true | true | true | true | true | true | true |
string(3) "1.0" | true | true | true | true | true | true | true | true | true | true | true |
string(3) "0x1" | true | true | true | true | true | true | true | true | true | true | true |
string(3) "0X1" | true | true | true | true | true | true | true | true | true | true | true |
string(3) "1e0" | true | true | true | true | true | true | true | true | true | true | true |
string(3) "1E0" | true | true | true | true | true | true | true | true | true | true | true |
string(5) "10e-1" | true | true | true | true | true | true | true | true | true | true | true |
string(6) "0.1e+1" | true | true | true | true | true | true | true | true | true | true | true |
10っぽい値の比較表も作ってみました。これは少し毛色が違います。
int(10) | string(3) "1e1" | string(4) "10e0" | string(4) "10a0" | string(4) "10a1" | string(4) "10e1" | |
---|---|---|---|---|---|---|
int(10) | true | true | true | true | true | false |
string(3) "1e1" | true | true | true | false | false | false |
string(4) "10e0" | true | true | true | false | false | false |
string(4) "10a0" | true | false | false | true | false | false |
string(4) "10a1" | true | false | false | false | true | false |
string(4) "10e1" | false | false | false | false | false | true |
長々と解説をするより比較表を眺めてもらった方がインパクトがありそうですので、細かい説明は省略します。興味のある方は参考リンクを見てください。また、自分なりの比較表を作りたい人のために、この表を出力する投げやりなPHPスクリプトも公開します。楽しい表が出来たら僕にも見せてください。(ダウンロード:php-equal-test.tgz)
補足しておくと、PHPには===演算子というのがあります。これは型が同じで値が等しい場合にのみtrueになる演算子です。2年前から僕の主張は「==はキモくてバグのもとだから===使おうよ」です。
本記事の内容は僕の中では今更な内容ですし、他の方も何度も言及している内容ではあるんですけど、「初めて知ったよ!」というPHPの人も居るんじゃないでしょうか。
また、今回の話題は他の言語の人にとっては衝撃的な内容ではないでしょうか。round関数の話題と違って、これは言語のコアと言って良い場所のビミョーな点ですから、PHPをDISりたい人たちにはナイスな話題かもしれませんね。(なんて煽ってみたりして)
参考リンク
マニュアル以外は全部自分の記事へのリンクです。PHPの==演算子関連でこんなに記事を書いているとは、自分でもビックリです。
- PHPマニュアル - 比較演算子
- PHP勉強会/さまざまな値の比較表 - 2年前の発表資料ですが、SPAMがウザいのでサイトごと消す予定です。
- ==で文字列同士を数値として比較する条件
- 「===」がtrueを返し「==」がtrueを返さないサンプル
- PHPの==演算子と巨大な16進数
- 『プログラミングPHP』の間違いを発見した