TypechoJoeTheme

尘遇 logo

当我遇上你
排行榜
统计

基于本地数据库的 IP 地址查询 PHP 源码

醉书生站长
1周前
0 评论
21 阅读
检测收录...
19:42

使用第三方的接口很方便,但也容易失效导致无法使用。来分享一个基于本地数据库的 IP 地址查询源码!

模块代码

<?php
/**
 * 纯真 IP 数据库查询 
 * 
 * 参考资料:
 * - 纯真 IP 数据库 http://www.cz88.net/ip/
 * - PHP 读取纯真IP地址数据库 http://ju.outofmemory.cn/entry/42500
 * - 纯真 IP 数据库自动更新文件教程 https://www.22vd.com/40035.html
 * - IpLocation https://github.com/nauxliu/IpLocation/
 * - 基于本地数据库的 IP 地址查询 PHP 源码 https://mkblog.cn/?p=1951
 * 
 * 使用示例:
 *   $ip = new IPQuery();
 *   $addr = $ip->query('IP地址');
 *   print_r($addr);
 */
 
class IPQuery {
    private $fh;        // IP数据库文件句柄
    private $first;     // 第一条索引
    private $last;      // 最后一条索引
    private $total;     // 索引总数
    private $dbFile = __DIR__ . DIRECTORY_SEPARATOR . 'qqwry.dat';      // 纯真 IP 数据库文件存放路径
    private $dbExpires = 86400 * 10;        // 数据库文件有效期(10天)如无需自动更新 IP 数据库,请将此值改为 0
    
    // 构造函数
    function __construct() {
        // IP 数据库文件不存在或已过期,则自动获取
        if(!file_exists($this->dbFile) || ($this->dbExpires && ((time() - filemtime($this->dbFile)) > $this->dbExpires))) {
            $this->update();
        }
    }
    
    // 忽略超时
    private function ignore_timeout() {
        @ignore_user_abort(true);
        @ini_set('max_execution_time', 48 * 60 * 60);
        @set_time_limit(48 * 60 * 60);    // set_time_limit(0)  2day
        @ini_set('memory_limit', '4000M');// 4G;
    }
    
    // 读取little-endian编码的4个字节转化为长整型数
    private function getLong4() {
        $result = unpack('Vlong', fread($this->fh, 4));
        return $result['long'];
    }
    
    // 读取little-endian编码的3个字节转化为长整型数
    private function getLong3() {
        $result = unpack('Vlong', fread($this->fh, 3).chr(0));
        return $result['long'];
    }
    
    // 查询位置信息
    private function getPos($data = '') {
        $char = fread($this->fh, 1);
        while (ord($char) != 0) {   // 地区信息以 0 结束
            $data .= $char;
            $char = fread($this->fh, 1);
        }
        return $data;
    }
    
    // 查询运营商
    private function getISP() {
        $byte = fread($this->fh, 1);    // 标志字节
        switch (ord($byte)) {
            case 0: $area = ''; break;  // 没有相关信息
            case 1: // 被重定向
                fseek($this->fh, $this->getLong3());
                $area = $this->getPos(); break;
            case 2: // 被重定向
                fseek($this->fh, $this->getLong3());
                $area = $this->getPos(); break;
            default: $area = $this->getPos($byte); break;     // 没有被重定向
        }
        return $area;
    }
    
    // 检查 IP 格式是否正确
    public function checkIp($ip) {
        $arr = explode('.', $ip);
        if(count($arr) != 4) return false;
        for ($i = 0; $i < 4; $i++) {
            if ($arr[$i] < '0' || $arr[$i] > '255') {
                return false;
            }
        }
        return true;
    }
    
    // 查询 IP 地址
    public function query($ip) {
        if(!$this->checkIp($ip)) {
            return false;
        }
    
        $this->fh    = fopen($this->dbFile, 'rb');
        $this->first = $this->getLong4();
        $this->last  = $this->getLong4();
        $this->total = ($this->last - $this->first) / 7;    // 每条索引7字节
        
        $ip = pack('N', intval(ip2long($ip)));
        
        // 二分查找 IP 位置
        $l = 0;
        $r = $this->total;
        while($l <= $r) {
            $m = floor(($l + $r) / 2);     // 计算中间索引
            fseek($this->fh, $this->first + $m * 7);
            $beginip = strrev(fread($this->fh, 4)); // 中间索引的开始IP地址
            fseek($this->fh, $this->getLong3());
            $endip = strrev(fread($this->fh, 4));   // 中间索引的结束IP地址
            
            if ($ip < $beginip) {   // 用户的IP小于中间索引的开始IP地址时
                $r = $m - 1;
            } else {
                if ($ip > $endip) { // 用户的IP大于中间索引的结束IP地址时
                    $l = $m + 1;
                } else {            // 用户IP在中间索引的IP范围内时
                    $findip = $this->first + $m * 7;
                    break;
                }
            }
        }
        
        // 查找 IP 地址段
        fseek($this->fh, $findip);
        $location['beginip'] = long2ip($this->getLong4());   // 用户IP所在范围的开始地址
        $offset = $this->getlong3();
        fseek($this->fh, $offset);
        $location['endip'] = long2ip($this->getLong4());     // 用户IP所在范围的结束地址
        
        // 查找 IP 信息
        $byte = fread($this->fh, 1); // 标志字节
        switch (ord($byte)) {
            case 1:  // 都被重定向
                $countryOffset = $this->getLong3(); // 重定向地址
                fseek($this->fh, $countryOffset);
                $byte = fread($this->fh, 1); // 标志字节
                switch (ord($byte)) {
                    case 2: // 信息被二次重定向
                        fseek($this->fh, $this->getLong3());
                        $location['pos'] = $this->getPos();
                        fseek($this->fh, $countryOffset + 4);
                        $location['isp'] = $this->getISP();
                    break;
                    default: // 信息没有被二次重定向
                        $location['pos'] = $this->getPos($byte);
                        $location['isp'] = $this->getISP();
                    break;
                }
            break;
            
            case 2: // 信息被重定向
                fseek($this->fh, $this->getLong3());
                $location['pos'] = $this->getPos();
                fseek($this->fh, $offset + 8);
                $location['isp'] = $this->getISP();
            break;
            
            default: // 信息没有被重定向
                $location['pos'] = $this->getPos($byte);
                $location['isp'] = $this->getISP();
            break;
        }
        
        // 信息转码处理
        foreach ($location as $k => $v) {
            $location[$k] = iconv('gb2312', 'utf-8', $v);
            $location[$k] = preg_replace(array('/^.*CZ88\.NET.*$/isU', '/^.*纯真.*$/isU', '/^.*日IP数据/'), '', $location[$k]);
            $location[$k] = htmlspecialchars($location[$k]);
        }
        
        return $location;
    }
    
    // 更新数据库 https://www.22vd.com/40035.html
    public function update() {
        $this->ignore_timeout();
        $copywrite = file_get_contents('http://update.cz88.net/ip/copywrite.rar');
        $qqwry     = file_get_contents('http://update.cz88.net/ip/qqwry.rar');
        $key       = unpack('V6', $copywrite)[6];
        for($i = 0; $i < 0x200; $i++) {
            $key *= 0x805;
            $key ++;
            $key = $key & 0xFF;
            $qqwry[$i] = chr(ord($qqwry[$i]) ^ $key);
        }
        $qqwry = gzuncompress($qqwry);
        file_put_contents($this->dbFile, $qqwry);
    }
    
    // 析构函数
    function __destruct() {
        if($this->fh) {
            fclose($this->fh);
        }
        $this->fp = null;
    }
}

使用方法

将上面的模块代码保存为 IPQuery.class.php,然后按照如下方法调用即可:

<?php
require_once('IPQuery.class.php');
 
$ip = new IPQuery();
$addr = $ip->query('123.233.233.233');
 
echo "<pre>
IP起始段:{$addr['beginip']}
IP结束段:{$addr['endip']}
实际地址:{$addr['pos']}
运 营 商:{$addr['isp']}
</pre>";

注意事项

本模块会在第一次被调用时自动从纯真网下载最新的 IP 数据库到本地,因此第一次进行查询时会有点慢。如果你的服务器因为某些原因,无法连接到纯真网获取数据库,可以直接下载离线版,并将 IPQuery.class.php 第 25 行的 $dbExpires 值改为“0”(即永不自动更新数据库)。

本文共590个字数,您已阅读:平均阅读时长≈2分钟。
php
朗读
点赞
0
支持
海报
欢迎
要时间嘉许,等春风得意,我正好遇上你!

微信/QQ/支付宝三合一收款码

微信
QQ
支付宝
打开微信/QQ/支付宝扫一扫,即可进行扫码打赏!
版权属于:

尘遇

最后修改:

2021 年 05 月 03 日 07 : 42 PM

本文链接:

https://chenyu.me/1369.html(转载时请注明本文出处及文章链接)

暂无评论

光阴者,百代之过客

今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月

最新回复

  1. kzkLv.1
    2021-05-10

    {!{data:image/webp;base64,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}!}

  2. 遇上你Lv.1
    2021-05-08
  3. AlanineLv.1
    2021-05-04
  4. 曾经Lv.1
    2021-05-04
  5. 郎君Lv.1
    2021-05-04