PHP の演算子のちょっとした振る舞いの違いに要注意
ダイエット再開したけど、すでに挫折気味の kimoto です。尊敬する人は、ダイエット成功した昔の自分です。
突然ですが、
$i++;
echo $i;
と
$i = $i + 1;
echo $i;
で結果変わる事がある、というのを知ってますか?
ちょっとした小ネタですが、これが意外と曲者だったので記事にしようと思います。
マニュアルの加算子のページを見ても、「$a を返し、$a に1を加えます。」となっているだけで、やってることは同じように見えます。
しかし、この2つ、結果が違ってくることがあるのです。
見て分かる通り、この2つのプログラムには最初の $i に何かを代入する所を端折っています。
お察しの通り、ここが普通の数値であれば問題ないです。が、ちょっと特殊な物が入るとおかしな事になってしまいます。
PHP の便利なところでもあり問題点も多いところの「型の自由さ」という部分が、悪さをする例をご紹介します。
通常の数値の場合
まず、普通の数値
$i = 1;
$i++;
echo $i;
$j = 1;
$j = $j + 1;
echo $j;
「1」を素直に数値で扱った場合。
これはもちろん、どちらも 2 が出力されます。
数値と解釈可能な文字列の場合
次に、数値に変換可能な文字列が入った場合。
$i = '1';
$i++;
echo $i;
$j = '1';
$j = $j + 1;
echo $j;
クォーテーションで囲うことで「1」を数値ではなく文字列で扱います。
この場合、文字列である「1」を PHP がよしなに対応してくれ、どちらも 2 が出力されます。
数値と解釈可能だけど文字列も入ってる場合
そして次。
$i = '1
';
$i++;
echo $i;
$j = '1
';
$j = $j + 1;
echo $j;
問題はこれ。「1」の後ろに改行が入ってる状態です。
この場合の出力結果は、$i は「1(改行)」のまま、$j は「2」が出力されます。
違う結果が出力されてしまいました。これはなぜか?
PHP の場合、文字列を数値にキャストすると「有効な数値データから始まる場合はその値が使用され、それ以外は 0 となる」という法則があります。
ex.マニュアル・文字列の数値への変換
つまり、「$j + 1」の結果が「2」となるのは、「1(改行)」を数値の「1」にキャストしてから演算をした、ということになります。
そして、「$i++」が「2」にならず「1(改行)」となるのは、文字列が入っている場合にキチンとキャストしてくれないから、ということになります。
普通にプログラムを組んでて改行が含まれてしまう事などあまりない…とも言い切れません。例えば、fget でファイルを読み込んだ時です。
ファイルに数値を書いておき、それをデータとして読み込み、カウントアップしたうえで書き込む、というカウンターっぽい物があったとすると、そのデータファイルに改行が入ってる時点でこの問題が起きる可能性があります。
計算する場合は明示的に型キャスト、が鉄則
まあ何にせよ、「数値として扱いたい時は、ちゃんとキャストしてから扱え!」やはりこれが鉄則となりますね。
$i = '1
';
$i = (int)$i;
$i++;
echo $i;
問題となった箇所も、これなら正常に計算されます。
加算子(++)の話
さて、ではなぜ「$i++」は「$i + 1」と違ってまず最初にキャストしてくれないのか?
ということで色々試してたらちょっとビックリしてしまいました。
$i = 'a';
$i++;
echo $i;
これ、何が出力されると思いますか?答えは「b」なんです。知ってました?
いや、マニュアルに載ってるのでもちろん周知の事実ではあると思うのですが、僕は全く知りませんでした。
いやー驚いた。これ、どこか使いドコロあるんかな。そしてこれ、減算はできません。ますます使いドコロが難しい。