PHPプロ!TIPS+

比較の落とし穴

PHPで変数などの比較をする際、おのおのが別の型同士だった場合にちょっと躓きやすい落とし穴があるのをご存知でしょうか?
PHPに少しずつ慣れてきた人は、比較する時に起こる型変換のルールを知っておくと未然にミスが防げたりするかも知れません。

まずは簡単に。以下のスクリプトを実行した際の表示内容はどうなるでしょう?

<?php
    $foo 
"test";

    if (
$foo == 0) {
        print 
"true";
    } else {
        print 
"false";
    }
?>

「true」と答えた方は、今回のTipsの趣旨は既にご理解なさっている方です。今回のTipsはご確認程度で見てもらえればと思います。
もし「false」と答えた方は、是非今回の落とし穴を知っていって下さい。ちなみに上のスクリプトの実行結果では「true」と表示されます。

では何故こうなるのでしょう?

その答えは、比較部分にあります。

上のスクリプトでは、$fooに代入されている文字列 'test' と整数の 0 を比較しています。
型的に言えば、$fooは文字列のstring、0は整数のintegerです。PHPでは以下のルールがあります。

整数値を文字列と比較する際、文字列が数値に変換されます。

『PHPマニュアル:比較演算子』
http://www.php.net/manual/ja/language.operators.comparison.php

さて、ここで言う「数値に変換される」という部分が問題です。
詳しいルールは以下の参考URLからご覧いただくとしまして、今回関係する部分を大雑把に書きますと

「文字列の最初の部分が、有効な数値データで始まるもの以外は 0 となる」

となります。

『PHPマニュアル:文字列の変換』
http://www.php.net/manual/ja/language.types.string.php#language.types.string.conversion

"test" という文字列は、最初の部分が英文字の t ……すなわち数字としては有効では無いので実際に比較する際には 0 として扱われるのです。

比較時のこの決まりを知らないと、意図していなかった動きをする場合が出てきます。マニュアル内でもswitch文の例が挙げられていますが

<?php
    $foo 
"test";

    switch (
$foo) {
        case 
0:
            print 
'$fooは 0 です。';
            break;
        case 
"test":
            print 
'$fooは test です。';
            break;
        default:
            print 
'$fooは 0 でも test でもありません。';
    }
?>

最初に$fooに"test"という文字列を代入します。

「$fooは test です。」

と表示される事を意図してこのようにプログラムコードを書いたとしても、実際には

「$fooは 0 です。」

と表示されます。

文字列と整数を比較しているため、文字列が数値に変換され、0として扱われるためです。

一方、

case 0:

と書いている部分を

case '0':

などと書くと、これは「文字列」の 0 を示すため、文字列と文字列の比較が行われます。

この時、比較する型が一緒のため数値に変換されるという事は無く、純粋にそのまま比較が行われるためこの部分では一致せず、次の「 case "test": 」の所で真になり「$fooは test です。」と表示されます。

もう一つ同じような例を示します。

<?php
    $ary 
array(012);

    
$foo "test";

    if (
in_array($foo$ary)) {
        print 
"発見!";
    } else {
        print 
"見つかりませんでした…";
    }
?>

in_array関数は、配列に値があるかチェックする関数です。
上の例では test という文字列が $ary の中に値として存在するかチェックしています。

普通に考えれば「見つかりませんでした…」と表示したいハズなのですが実際に動かしてみればわかりますが「発見!」と表示されてしまいます。
配列の中に整数の 0 があるため、数値変換された際に0になる文字列はこぞって引っかかってしまう事になります。

余談ですが、このin_array関数もそうですがいくつかの関数は、引数を追加して指定する事で比較時に「型」も確認します。
この場合、完全に型までも一緒でなければいけなくなり、判定を厳格にする事ができます。

以下、型も比較する例です。

<?php
    $ary 
array(012);

    
$foo "test";

    if (
in_array($foo$arytrue)) {
        print 
"発見!";
    } else {
        print 
"見つかりませんでした…";
    }
?>

このようにすると実行結果の出力は「見つかりませんでした…」となります。

バックナンバーについて

TIPS-MLは、毎週金曜日に更新され、新しい記事が掲載されます。

Tipsꗗy[W 

Pick Up Q&A

Q
ログファイルの中の空のデータ行を削除したい
 このエントリーをはてなブックマークに追加 
A
ログのデータ個数(列数)が固定で、空のログが"<><><>"だと既知であれば if ($line === "<><><>") { continue; } で読み飛ばしてもいいのでは? ...

>>続きを読む

まずは配列や文字列の扱いから、じっくり勉強して行きましょう。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)