基于-纯-真-本地数据库的 IP 地址查询 PHP 源码
【摘要】
很多第三方的 IP 地址查询 API 接口,直接调用第三方的接口很方便,但也容易失效导致无法使用。因此今天来分享一个基于本地数据库的 IP 地址查询源码(亲测可行)!
模块代码
<?php/** * 纯真 IP 数据库查询 * 使用示例: *...
很多第三方的 IP 地址查询 API 接口,直接调用第三方的接口很方便,但也容易失效导致无法使用。因此今天来分享一个基于本地数据库的 IP 地址查询源码(亲测可行)!
模块代码
-
<?php
-
/**
-
* 纯真 IP 数据库查询
-
* 使用示例:
-
* $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,然后按照如下方法调用即可:
-
require_once('IPQuery.class.php');
-
-
$ip = new IPQuery();
-
$addr = $ip->query('123.223.233.233');
-
-
echo "<pre>
-
IP起始段:{$addr['beginip']}
-
IP结束段:{$addr['endip']}
-
实际地址:{$addr['pos']}
-
运 营 商:{$addr['isp']}
-
</pre>";
输出效果如下所示:
注意事项
本模块会在第一次被调用时自动从纯真网下载最新的 IP 数据库到本地,因此第一次进行查询时会有点慢。如果你的服务器因为某些原因,无法连接到纯真网获取数据库,可以直接下载离线版,并将 IPQuery.class.php
第 25 行的 $dbExpires
值改为“0”(即永不自动更新数据库)。
在[ 码农编程进阶笔记 ]公众号回复【激活码】关键字,获取最新[全家桶][webstorm][pycharm]激活码百度盘链接
文章来源: blog.csdn.net,作者:lxw1844912514,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/lxw1844912514/article/details/126515923
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)