…久しぶりの更新、少しずつでもちゃんと更新していこう、、
ってのは置いといて、今回は、PHPでのキャストのお話。
PHPの様なスクリプト言語は、型を明示して使いはじめる必要がないです。
例えば下の様に、変数にテキトーに値を入れた後に、型を調べてみると(gettype)、自動的に適当な型を当てはめてくれているのがわかります。(まぁダブルクオートで囲われていたら文字列としてくるだけですが)
php > $text = "test"; php > $int = 5; php > echo gettype($text); string php > echo gettype($int); integer
なので、下記のような感じで、数値をダブルクオートで囲うと、当然文字列の扱いとなります。
php > $one = "1"; php > $two = "2"; php > echo gettype($one); string php > echo gettype($two); string
もちろん上記変数同士を足しあわせた場合は、数値として扱ってくれ、加算処理を行ってくれます。
php > $three = $one + $two; php > echo $three; 3
そしてこの時、代入した値の型を調べてみると、数値型となります。
php > echo gettype($three); integer
この時に行われている、文字列→数値が、いわゆるキャスト。まぁ型の相互変換です。
まぁ基本的な機能である、キャストですが、これ比較演算するときにも勝手にやってくれます。
とても便利です。
が、罠もあります。
例えばこんな時。
php > $stringTen = "ten"; php > $numberTen = 10; php > echo ($stringTen == $numberTen);
これは当たり前ですが、trueは返しません。
が、例えば次の様な場合は問題が発生します。
php > $stringZero = "zero"; php > $numberZero = 0; php > echo ($stringZero == $numberZero); 1
前述の「文字列の10」と「数値の10」の時の、0バージョンです。
入れる値を10から0に変えただけです。
しかし!
等式はtrueを返しています。
普通、こんなプログラムを書くことはそうそうないと思いますが(というか書かないで欲しいですが)、
文字列を返すような関数を作成したときに、false(boolean値)返すのも気持ち悪いということで、0を返して、呼び出し先で0だったら〜みたいな処理を書いた日には、地獄です。
バグ作っちゃいます。
例えばこんなかんじのクソみたいなコードを書くとします。
php > function test($number){ php { $a = array(1=>"one", 2=>"two"); php { if (array_key_exists($number, $a)) php { { php { return $a[$number]; php { } php { return 0; php { } php > $b = test(3); php > if ($b == 0) echo "yes"; yes
上記では、test関数に3を渡しているので、yesが表示されるのは自然です。
が、test関数に1を渡して同じように比較してみましょう。
php > $c = test(1); php > if ($c == 0) echo "yes"; yes
はい、期待どおりにならず、yesが表示されました。
ちなみに、上記の変数$cを、echoしてみると、こんなかんじです。
php > echo $c; one
つまり、”one” == 0 で、trueが返ってるんですね。
最初のstringZero==numberZeroでtrueが返っているのと同じですね。
なぜこうなるか?
このからくりを追ってみましょう。
まずphpの公式ドキュメントで、比較演算子の役割をみてみましょう。
http://www.php.net//manual/ja/language.operators.comparison.php
$a == $b 等しい 型の相互変換をした後で $a が $b に等しい時に TRUE。」
と書いてあります。
「型の相互変換をしたあとで」
↑ここが肝です。
つまり先にキャストの処理が行われます。
そして、キャストのドキュメントをみると、
http://www.php.net/manual/ja/language.types.string.php#language.types.string.conversion
文字列の最初の部分により値が決まります。文字列が、 有効な数値データから始まる場合、この値が使用されます。その他の場合、 値は 0 (ゼロ) となります。 (中略) この変換に関する詳細は、Unix のマニュアルページで strtod(3) を参照ください
なので、数値へ変換できない文字列は、「0(ゼロ)」を返してしまうよ。
ということです。
Unixのstrtod(3)に準拠していますよということ。
http://www.nxmnpg.com/ja/3/strtod
結果的に、文字列を数値に変換できなかったため、0を返してきて、
それを理解せずに、プログラマが “zero”==0 などという等式を当てはめてしまったがために、
かならずtrueを通ってしまうということでした。
まぁphpを扱うプログラマとしては、当然知っておくべきことで、
プログラマ、エンジニアとしては、ナンセンスな書き方をしていると、こんなことが起こるよ。
ということでした。
。。。ま、とあるプログラムでやってしまったんですけどね。
もうしないという戒めを込めての、メモでした。