…久しぶりの更新、少しずつでもちゃんと更新していこう、、

ってのは置いといて、今回は、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を扱うプログラマとしては、当然知っておくべきことで、
プログラマ、エンジニアとしては、ナンセンスな書き方をしていると、こんなことが起こるよ。
ということでした。

。。。ま、とあるプログラムでやってしまったんですけどね。
もうしないという戒めを込めての、メモでした。

プログラミングPHP 第3版

投稿者 iyken

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です