根据 LumaQQ 开发者文档中的纯真 IP 数据库格式详解,我编写了一个 PHP 的查询 IP 所在地区信息的类。在编写过程中发现纯真 IP 数据库格式详解中关于记录区的描述不是很全面,不过出入也不是很大,所以我没必要再写一份纯真 IP 数据库的格式说明了,大家感兴趣的话,读一读下面的代码应该就能看出来了。代码中加了很详细的注释,应该很容易读懂的。
在创建这个类的一个实例后,实例中就保存了打开的文件指针和一些查询需要的信息,每次查询时不需要重新打开文件,直到页面执行结束后,打开的文件才会自动关闭。这样。在一个页面内进行多次查询时,效率是很高的。并且此类不仅可以直接查询 IP,还可以自动将域名解析为 IP 进行查询。
下面是程序代码:
/**
* IP 地理位置查询类
*
* @author 马秉尧
* @version 1.5
* @copyright 2005 CoolCode.CN
*/
class IpLocation {
/**
* QQWry.Dat文件指针
*
* @var resource
*/
var $fp;
/**
* 第一条IP记录的偏移地址
*
* @var int
*/
var $firstip;
/**
* 最后一条IP记录的偏移地址
*
* @var int
*/
var $lastip;
/**
* IP记录的总条数(不包含版本信息记录)
*
* @var int
*/
var $totalip;
/**
* 返回读取的长整型数
*
* @access private
* @return int
*/
function getlong() {
//将读取的little-endian编码的4个字节转化为长整型数
$result = unpack('Vlong', fread($this->fp, 4));
return $result['long'];
}
/**
* 返回读取的3个字节的长整型数
*
* @access private
* @return int
*/
function getlong3() {
//将读取的little-endian编码的3个字节转化为长整型数
$result = unpack('Vlong', fread($this->fp, 3).chr(0));
return $result['long'];
}
/**
* 返回压缩后可进行比较的IP地址
*
* @access private
* @param string $ip
* @return string
*/
function packip($ip) {
// 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
// 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
return pack('N', intval(ip2long($ip)));
}
/**
* 返回读取的字符串
*
* @access private
* @param string $data
* @return string
*/
function getstring($data = "") {
$char = fread($this->fp, 1);
while (ord($char) > 0) { // 字符串按照C格式保存,以\0结束
$data .= $char; // 将读取的字符连接到给定字符串之后
$char = fread($this->fp, 1);
}
return $data;
}
/**
* 返回地区信息
*
* @access private
* @return string
*/
function getarea() {
$byte = fread($this->fp, 1); // 标志字节
switch (ord($byte)) {
case 0: // 没有区域信息
$area = "";
break;
case 1:
case 2: // 标志字节为1或2,表示区域信息被重定向
fseek($this->fp, $this->getlong3());
$area = $this->getstring();
break;
default: // 否则,表示区域信息没有被重定向
$area = $this->getstring($byte);
break;
}
return $area;
}
/**
*指定されたIPアドレスまたはドメイン名に基づいてリージョン情報を返します
*
* @Accesspublic
* @param String $ip
*/
function getlocation($ip) {
if (!$this->fp) return null; // データ ファイルが正しく開かれていない場合は、直接 null を返します
$location[ 'ip'] = gethostbyname($ip); // 入力されたドメイン名を IP アドレスに変換します
$ip = $this->packip($location['ip']); // 入力された IP アドレスを変換します比較可能なIPアドレス
/ /検索の上部boundary使用 - 使用する最後のIPレコードを使用して使用しますビッグエンディアン形式に変換
// 比較のため、同じものです。 P IF ($ ip & lt; $ beginip) {// ユーザーの IP が中間レコードの開始 IP アドレスより小さい
$ u = $ i -1 // 中間レコードの検索の上限を次のように変更します。 1 つ減らす}}
else
else {
fseek($this->fp, $this->getlong3()) if ($ip > $endip) { // ユーザーの IP が末尾より大きい場合中間レコードの IP アドレス
// ユーザーの IP が中間レコードの IP 範囲内にある場合️
}
}
/ ip 範囲の開始アドレス
Fseek ($ this-& gt; FP, $ Offset); //のユーザー IP が存在する範囲の終了アドレス
$byte = fread($this->fp, 1); // フラグバイト
フラグバイトは 1 で、国と地域の情報が両方とも同時にリダイレクトされることを示します。時間
$ ; fseek ($this->fp, $this->getlong3()); リダイレクトされません。 ;getstring($byte); break;
ケース 2: フラグ バイトは 2 で、国情報が再作成されました オリエンテーション
$location['area] '] = $this->getarea();
デフォルト: '''' 's ;
}
if ($ local ['country'] == "cz88.net") {// cz88.net は有効な情報がないことを示します
$ local [ 'Country'] = "nown"; ") {
$this->fp = 0;
if (($this->fp = @fopen($filename, 'rb')) !== false) {
$this->firstip ;lastip - $this->firstip) / 7;
/**
* コンストラクター、QQWry.Dat ファイルを開いてクラス内の情報を初期化します
*/
function _IpLocation() { if ($this->fp) {
} $this->fp = 0;
}
}
?>