hnwの日記

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の==演算子関連でこんなに記事を書いているとは、自分でもビックリです。