PHPプロ!TIPS+

IPアドレスの保存方法

IPアドレスをデータベースに保存する場合に、文字列で保存してないでしょうか。IPアドレスは32bitの数値なので、そのまま数値として保存することができます。

一般的にIPアドレスはxxx.xxx.xxx.xxxというような4つの数値をドットで区切った文字列で表されることが多いと思います。この文字列は32bitの整数を8bitずつに区切った数値を並べたもので、この数値から32bitの整数を取得することができます。

PHPでは、ip2longlong2ip という2つの関数を使って、文字列形式から数値に、数値から文字列形式に変換することができます。例えば、Webページにアクセスしてきた端末のIPアドレスを数値形式で取得する場合、以下のようにします。

$ip_address = $_SERVER["REMOTE_ADDR"];
$ip_long = ip2long($ip_address);

この$ip_longが数値としてのIPアドレスになるわけです。

データベースにIPアドレスを保存する場合、最大15文字の文字列を整数で保存できるので、データ量はかなり減らすことができます。(最近ではデータサイズを気にすることはないかもしれませんが。。。

また、検索を行う場合はビット演算を行うことになります。完全一致の場合は等号「=」での検索になるので、あまり関係ないかもしれませんが、ネットワークアドレスでの検索を行う場合は部分一致になります。

たとえば、あるIPアドレスが192.168.100.0/24 のネットワークに含まれるかどうかを検索する場合は以下のようになります。

$ip_address = $_SERVER["REMOTE_ADDR"];
$ip_long = ip2long($ip_address);

$network = ip2long("192.168.100.0");
$netmask = 24;

if ( $ip_long >> (32 - $netmask) == $network >> (32 - $netmask) ) {
  // $network のネットワークに含まれない
} else {
  // $network のネットワークに含まれる
}

ネットマスクが24のネットワークなので後半8ビットは比較対象には含まれません。そのため、右シフトをその回数分して、同様に右シフトしたネットワークアドレスと一致するかどうかを見れば判定することができます。

少々ややこしいのですが、192.168.100.0/25 のネットワークなど、中途半端なネットワークアドレスについての検索を行う場合はビット演算でないと難しいでしょう。

また、ビット演算は色々な場面で使うことができます。例えば、曜日を表す場合にも使用することができます。あるデータに対して曜日ごとに on/off の状態を付加することを考えると、7bitの数値として考えることができます。日曜日を0土曜日を2 ^ 6とすると、平日は 0111110 の2進数で表すことができます。月曜日は 0000010 となります。

こうすると、月曜日が含まれるかどうかは & を使用して判定することができます。

$week = decbin("0111101");
if ($week & decbin("0000010")) {
  // 月曜日が含まれる
} else {
  // 含まれない
}

このようにビット演算では32bitがintのビット数であれば、32種類までフラグのon/off を1つの数値で表すことができます。

バックナンバーについて

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

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