php实用纯真ip数据库获取访客所在地
IPQuery.class.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | <?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,然后按照如下方法调用即可:
1 2 3 4 5 6 7 8 9 10 11 12 | <?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”(即永不自动更新数据库)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
2021-07-20 thinkphp6使用jwt