PHPの比較演算子 "文字列" == 0 は真である。

知っている人はゴメンなさい。宗教論争です。
知らない人は取り敢えず、何も言わずにこのコードを試してください。

<?php
if ("文字列" == 0) {
  echo 1;
} else {
  echo 2;
}

結果は、1が出力されたと思います。
いやいや、"文字列"は0じゃないよ!と賛同してくれた人は続きをどうぞ。

内製フレームワークで、バリデーションを行っていて気づいたのですが、どうも不思議な挙動をしていました。

内容としては入力された値が、入力可能な値(今回は時差)の中から選択されているかのバリデーションです。(範囲チェックではなくあくまで利用可能な数値であるかのチェック。3.1415とか入力されても困るので)

正常系は動いたので、念のため限界値チェックや文字種のチェックをしていて、日本語の文字列でチェックしていると、何度やっても正常として返ってきてしまう。バリでションのコードは次のようなものです。

<?php
// 色々省略
class Validation {
  function valid_allow($needle, $array = array()) {
    if (in_array($needle, $array) {
      return TRUE;
    } else {
      return FALSE;
    }
}
$valid = new Validation();
if (!$valid->valid_allow("日本語", array(-13, -12 ... 0, 1 ... 12)) {
  echo "Error!!";
} else {
  echo "OK!!";
}

うっそーん。そんなバカな・・・。と何度も確認していくうちに、どうも数値0が検索先の配列にあると失敗する気がすることに気づきました。そこで、最初に書いたコードを記述して試したのですが、どうやら「"文字列" == 0 は真である」と。

はじめは、いやーナイナイそれは無い。あり得ない。マジナイっすわwww(`ェ´)ピャーなどと、完全に頭が逝かれてしまいましたが、その現象は、php.net でも度々コメント投稿されているようでした。
http://jp.php.net/manual/ja/function.in-array.php

色々、追ううちに、これは”不具合”ではないことはわかりました。PHPにはご存知のとおり明示的に型を宣言する必要はありません。そして、比較演算子も、その言語仕様に合わせるように、"0" == 0 などの型が異なる比較がある場合、暗黙的にキャストして比較してくれているのです。

では、どうして、"文字列" == 0 が、真になるかということなんですが、先ほど書いたとおり、暗黙的に"文字列"は数値にキャストしようとします。この時、数値として変換できない"文字列"は、0 として扱われてしまうのです。当然ながら、"文字列" == "0", FLASE, "" は、偽 です。

<?php
var_dump("文字列" == 0);       // TRUE
var_dump("文字列" == "0");     // FALSE
var_dump("文字列" == FLASE);   // FALSE
var_dump("文字列" == "");      // FALSE

なるほど!!!理解した・・・・!?
いや待て。本当にそれでいいのか?今回言いたいのはそこです。

実際には === で型も意識した比較などは可能ですし、in_arrayでも、オプションで型まで比較することは可能です。それは知ってるんです。ただ、汎用的なバリデーションを作った(つもり)なので、値は、ファイル、DB、リクエストなどどんな値を使うか分からない。そのため、あえて指定したく無かったんです。

仮にチェックする前に型を変換することが前提になれば、延々とキャスト処理を書かなければならない。そんな事をやるのであれば、そもそもPHPなんて使わないってことになってしまう。

(ここですね。宗教戦争。他の言語のエンジニアもそこで議論がストップしてしまう。)

分かるんですが、私のいいたいのは例え暗黙的にキャストしていても、"文字列" == 0 は、偽であって欲しいんじゃないか?ということなんです。だってこの結果は誰も得しないでしょう?元々、型を意識しないというのが売りなんだし・・・。正直言語設計をできるエンジニアではないので、過去の経緯などはぶっちゃけ分かりません。

仕方がなく配列に数値の0がある場合だけ、そこだけ型を比較するという格好悪いコードを書く選択を取りました。(型比較もやってみたんですが案の定ほとんど動かなくなったw)

この件で、一日中もやもやしましたね。痛い目にあってよかったというべきなんでしょう。

ちなみに、以下はPHPの型比較表です。
http://jp.php.net/manual/ja/types.comparisons.php

上記のケースではのってませんが、暗黙的なキャストは可能なかぎり勝手に変換してくれるので、以下のような結果になりますよ!気をつけてくださいね。

<?php
var_dump("文字列" == 9);  // FALSE
var_dump("9文字列" == 9); // TRUE

それでも、PHPは回ってる。