PHPプロ!TIPS+

文字列から数値へのキャストの落とし穴

PHPで何気なく使用している等号・不等号ですが、文字列と数値を比較しようとすると予期せぬ動きをすることがあります。今回はおちいりがちな落とし穴を紹介します。

まずは簡単に以下の5つについての比較を行ってみましょう。以下の6つはすべてブール値としてみるとfalseとなる値です。

  1:  null
  2:  false
  3:  0 (integer)
  4:  "" (空文字)
  5:  "0" (string)
  6:  array()

これを「==」で比較してみると以下のような結果となります。

  |1|2|3|4|5|6
--------------------
1|○|○|○|○|×|○
2|○|○|○|○|○|○
3|○|○|○|○|○|×
4|○|○|○|○|×|×
5|×|○|○|×|○|×
6|○|○|×|×|×|○

×となっている組は(1,5), (3,6), (4,5), (4,6), (5,6)の組です。これ以外にも文字列から数値のキャストなどで、おかしな挙動が多いです。興味のあるかたはDinoのhanawa氏によるPHP勉強会の発表資料を見てみるとよいでしょう。

次に数値文字列の比較について考えましょう。以下のスクリプトを見てください。

if (0 < $num && $num < 100) {
  echo "0以上100以下";
}

この場合、$num に'0xa'や'1e1'などの文字列が入っていてもこのif文を通過してしまいます。さらにやっかいなのが、この変数を整数にキャストした場合です。

if (0 < $num && $num < 100) {
  echo "0以上100以下";
  echo "\n" . intval($num);
  echo "\n" . floatval($num);
}

先ほどの'1e1'の場合、floatval()関数で比較のときの変換と同じ値に変換することができますが、intval()関数では1に変換されてしまいます。これは(int)のキャストでも同じです。'0xa'の場合はintval()、floatval()の両方で0となってしまいます。

'0xa'を10に変換するのに以下のようにします。

if (0 < $num && $num < 100) {
  echo "0以上100以下";
  $num += 0;
  echo "\n" . intval($num);
  echo "\n" . floatval($num);
}

結局のところ、数値比較を行う場合はあらかじめintval()関数を使用してから行うのが鉄則ということですね。

インタラクティブなコマンドラインプログラム

他のプログラミング言語ではよく見かけますが、対話形式のコマンドラインプログラムを作りたいと思ったことはないでしょうか?

実はPHPでも対話形式のコマンドラインプログラムを作ることができます。

以下がサンプルプログラムです。

<?php
ob_end_clean
();
echo(
"足し算プログラム\n");
echo(
"一つ目の整数を入力してください。\n");

while (
true) {
  
$num1 fgets(STDIN10);

  
//デバッグ用に出力
  
var_dump($num1);

  
$num1 rtrim($num1"\n");

  if (
ctype_digit($num1)) {
    break;
  }
  echo 
"整数を入力して下さい。\n";
}

echo 
"二つ目の整数を入力してください。\n";
while (
true) {
  
$num2 rtrim(fgets(STDIN10), "\n");

  if (
ctype_digit($num2)) {
    break;
  }
  echo 
"整数を入力して下さい。\n";
}

echo 
"$num1 + $num2 = ";
echo (int)
$num1 + (int)$num2;
?>

fgets()関数の第一引数に標準入力(STDIN)を指定しているところがポイントで、fgets()関数を実行したところで入力受付状態になります。

以下、注意点です。

  • 出力バッファリングの無効化

出力バッファリングが有効になっているとプログラム終了まで何も表示されません。 サンプルプログラムではob_end_clean()で出力バッファリングを無効にしています。

他にも、このプログラム用のoutput_bufferingをOffに、output_handlerを指定しないphp.iniファイルを用意して、php -c オプションを使って実行する方法もあります。

  • 入力データの整形

fgets()関数は最後に改行が入るので、必要に応じて取り除くようにします。

コマンドラインプログラムを作ることがあったら、使ってみてはいかがでしょうか?

バックナンバーについて

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

Tipsꗗy[W 

Pick Up Q&A

Q
マジッククォートとmysql_real_escape_string
 このエントリーをはてなブックマークに追加 
A
magic_quotes_gpcでは、SQLインジェクション対処は十分できません。主な理由として、以下が上げられます。 ・magic_quotes_gpcは文字コードを考慮しないで処理するので、Shift_JISを使っている場合、SQLインジェ...

>>続きを読む

SQLインジェクション対策は時と場合で使う関数が変わります。その時にあったものを使いましょう。

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