PHPにおける数値と文字列のヘンな比較結果2007年12月16日 04時09分25秒

0 == 'str' // true

PHPSPOTでこんなエントリがあった。

タイトルだけみたときは「0 == '0'のことか?そりゃ当然じゃん」とか思って記事を見たところ、
$a = 0;
$b = 'str';

if( $a == $b ) {
	echo 'equal';
} else {
	echo 'not equal';
}
なんてコードが掲載されてた。試してみると確かに「equal」と出力される。どういうこっちゃ?

仕様を調べてみる

ちょっとこの現象は、どういう理屈に基づいてこうなるのかさっぱりわからないので、仕様上どうなっているのか調べてみた。

違う型同士を比較するので、暗黙の型変換が発生するだろうと思い、マニュアルの「型の相互変換」を見てみたがそれっぽいことは記述されていなかった。

このページの最後に参照リソースとして「PHP 型の比較表」があったのでそちらを見てみると、 「==による緩やかな比較」という表には確かに数値の「0」と文字列「php」の比較結果は「TRUE」になると記載されているので、今回の現象はどうやら仕様らしいことはわかった。が、納得いかん

で、他に関連しそうなところがないかと思って目次を探してたら、「比較演算子」の項があったので見てみると、

整数値を文字列と比較する際、文字列が 数値に変換されます。
とある。ってことは、文字列'str'が暗黙的に整数に変換されたものと整数0が比較されたということのようだ。

文字列 → 数値変換ってどうなっとるの?

これで足がかりが見つかったので、もう一度「型の相互変換」から追っかけてみたところ、「文字列」の「文字列変換」の節

文字列の最初の部分により値が決まります。文字列が、 有効な数値データから始まる場合、この値が使用されます。その他の場合、 値は 0 (ゼロ) となります。
とあった。

検証してみる

なるほど、上記仕様なら、文字列'str'は数値0と見なされるので、最初のコードで「等しい」と判断されるわけだ。ではそのとおりに振舞うか、テストしてみよう。

文字列の先頭に数字を入れればその数字が示す数値に変換されるはずだし、比較する数値を0以外にしても等値にならないはずなので、最初のコードを含めて3通り試してみた。

C:\Documents and Settings\dara-j>php -r "echo 0 == 'abc' ? 'equal' : 'not equal';"
equal
C:\Documents and Settings\dara-j>php -r "echo 0 == '1abc' ? 'equal' : 'not equal';"
not equal
C:\Documents and Settings\dara-j>php -r "echo 1 == 'abc' ? 'equal' : 'not equal';"
not equal
C:\Documents and Settings\dara-j>
おお、予想通り。やっと納得がいった。

結論

比較だけなのに勝手に文字列→数値変換が発生するので気をつけようってことね。しかしこれ、仕様としてはあまり行儀が良くない気がするなぁ...

コメント

_ anonymous ― 2008年02月12日 18時32分48秒

単純に===で比較すればよろしい

_ dara-j ― 2008年02月17日 13時16分01秒

おお、一週間も放置でごめんなさい。

確かに === で厳密な比較を行うのが確実なのですが、それには先に型変換を正しく行う必要がでてくるので、自分はあまり使用していません。キャストするとまた不可思議な動きをするし。

自動変換による落とし穴なんかはLLでは避けられないんでしょうけど、やっぱりPHPは感覚的にちょっと不思議な気がします。

_ がる ― 2008年02月19日 16時55分18秒

がると申します。
私も以前、このネタでBlogを書いていたのですが( http://d.hatena.ne.jp/gallu/20061108/p1 )。
多分「数値 or booleanの比較なら===を使って」「文字列の比較ならstrcmpを使う」のが一番安全なのではないかと考えております。

_ dara-j ― 2008年02月19日 22時36分23秒

がるさん、コメントありがとうございます。
ご紹介いただいたがるさんの記事は、この記事あげた直後にPHPSPOTさん経由で読ませていただき「ダダカブリやん!」と恥ずかしくなっておりました(^^;

> 多分「数値 or booleanの比較なら===を使って」「文字列の比較ならstrcmpを使う」のが一番安全なのではないかと考えております。
自分もそう思います。特に比較する値の型がどちらもはっきりしている場合は===を使うべきでしょうね。

個人的には大抵は比較に先んじて下処理(二重引用符で囲って強制文字列化とか、型を検査して対象外の型の場合はデフォルト値へ置き換え or 例外を投げるとか)をしてしのいでいますが。

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※なお、送られたコメントはブログの管理者が確認するまで公開されません。

名前:
メールアドレス:
URL:
コメント:

トラックバック

このエントリのトラックバックURL: http://dara-j.asablo.jp/blog/2007/12/16/2514789/tb

※なお、送られたトラックバックはブログの管理者が確認するまで公開されません。

_ A Day In The Boy's Life - 2007年12月16日 23時34分41秒

ずっと特定の言語を使っていたら、それに慣れてしまってあまり細かいルールを気にしなくなるのですが、
その考えが落とし穴になってしまったケース。

PJに参画してもらったJava使いの子にプログラム作らせたら無限ループを引き起こしました。
その記述は下記

for ($i = 0;

_ ito.tetsunosuke/notebook - 2007年12月17日 17時09分18秒

’abc’ == 0 になることなんてPHPをちゃんとやっていれば有名な事実なのですが これも気をつけたほうがいいと思います。 $ php -r "echo '011' == 9 ? 'equal':'not equal';" not equal $ php -r "echo '011' == 11 ? 'e