换个方式获取评论IP属地
This commit is contained in:
浪子 2024-12-13 11:20:00 +08:00
parent c7171fac9b
commit 696ef5a2e9
3 changed files with 183 additions and 152 deletions

View File

@ -111,7 +111,12 @@ function threadedComments($comments, $options) {
<span class="dot"></span>
<div class="comment--time"><?php $comments->date('Y-m-d H:i'); ?></div>
<span class="dot"></span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M12 20.8995L16.9497 15.9497C19.6834 13.2161 19.6834 8.78392 16.9497 6.05025C14.2161 3.31658 9.78392 3.31658 7.05025 6.05025C4.31658 8.78392 4.31658 13.2161 7.05025 15.9497L12 20.8995ZM12 23.7279L5.63604 17.364C2.12132 13.8492 2.12132 8.15076 5.63604 4.63604C9.15076 1.12132 14.8492 1.12132 18.364 4.63604C21.8787 8.15076 21.8787 13.8492 18.364 17.364L12 23.7279ZM12 13C13.1046 13 14 12.1046 14 11C14 9.89543 13.1046 9 12 9C10.8954 9 10 9.89543 10 11C10 12.1046 10.8954 13 12 13ZM12 15C9.79086 15 8 13.2091 8 11C8 8.79086 9.79086 7 12 7C14.2091 7 16 8.79086 16 11C16 13.2091 14.2091 15 12 15Z"></path></svg><?php echo convertip($comments->ip); ?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M12 20.8995L16.9497 15.9497C19.6834 13.2161 19.6834 8.78392 16.9497 6.05025C14.2161 3.31658 9.78392 3.31658 7.05025 6.05025C4.31658 8.78392 4.31658 13.2161 7.05025 15.9497L12 20.8995ZM12 23.7279L5.63604 17.364C2.12132 13.8492 2.12132 8.15076 5.63604 4.63604C9.15076 1.12132 14.8492 1.12132 18.364 4.63604C21.8787 8.15076 21.8787 13.8492 18.364 17.364L12 23.7279ZM12 13C13.1046 13 14 12.1046 14 11C14 9.89543 13.1046 9 12 9C10.8954 9 10 9.89543 10 11C10 12.1046 10.8954 13 12 13ZM12 15C9.79086 15 8 13.2091 8 11C8 8.79086 9.79086 7 12 7C14.2091 7 16 8.79086 16 11C16 13.2091 14.2091 15 12 15Z"></path></svg>
<?php
$ip = $comments->ip; // Ensure this is the correct property for IP
$location = get_ip_location($ip);
echo htmlspecialchars($location['country']);
?>
<span class="comment-reply-link u-cursorPointer">
<?php $comments->reply('<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" class="" ><g><path d="M12 3.786c-4.556 0-8.25 3.694-8.25 8.25s3.694 8.25 8.25 8.25c1.595 0 3.081-.451 4.341-1.233l1.054 1.7c-1.568.972-3.418 1.534-5.395 1.534-5.661 0-10.25-4.589-10.25-10.25S6.339 1.786 12 1.786s10.25 4.589 10.25 10.25c0 .901-.21 1.77-.452 2.477-.592 1.731-2.343 2.477-3.917 2.334-1.242-.113-2.307-.74-3.013-1.647-.961 1.253-2.45 2.011-4.092 1.78-2.581-.363-4.127-2.971-3.76-5.578.366-2.606 2.571-4.688 5.152-4.325 1.019.143 1.877.637 2.519 1.342l1.803.258-.507 3.549c-.187 1.31.761 2.509 2.079 2.629.915.083 1.627-.356 1.843-.99.2-.585.345-1.224.345-1.83 0-4.556-3.694-8.25-8.25-8.25zm-.111 5.274c-1.247-.175-2.645.854-2.893 2.623-.249 1.769.811 3.143 2.058 3.319 1.247.175 2.645-.854 2.893-2.623.249-1.769-.811-3.144-2.058-3.319z"></path></g></svg>'); ?>
</span>

View File

@ -1,6 +1,6 @@
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
require_once 'qqwry.php';
function themeConfig($form) {
$logoUrl = new Typecho_Widget_Helper_Form_Element_Text('logoUrl', NULL, NULL, _t('站点 LOGO 地址'));
$form->addInput($logoUrl);
@ -477,155 +477,17 @@ function commentApprove($widget, $email = NULL)
/** 获取评论者地址 */
function convertips($ip) {
$ip1num = 0;
$ip2num = 0;
$ipAddr1 = "";
$ipAddr2 = "";
$dat_path = dirname(__FILE__) . '/dist/qqwry.dat';
// 验证IP格式
if (!preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/", $ip)) {
return 'IPV6无法获取归宿地';
}
// 打开数据文件
if (!$fd = @fopen($dat_path, 'rb')) {
return 'IP 数据库路径不正确';
}
// 计算IP数值
$ip = explode('.', $ip);
$ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3];
$DataBegin = fread($fd, 4);
$DataEnd = fread($fd, 4);
$ipbegin = implode('', unpack('L', $DataBegin));
if ($ipbegin < 0) $ipbegin += pow(2, 32);
$ipend = implode('', unpack('L', $DataEnd));
if ($ipend < 0) $ipend += pow(2, 32);
$ipAllNum = ($ipend - $ipbegin) / 7 + 1;
$BeginNum = 0;
$EndNum = $ipAllNum;
// 二分查找IP
while ($ip1num > $ipNum || $ip2num < $ipNum) {
$Middle = intval(($EndNum + $BeginNum) / 2);
fseek($fd, $ipbegin + 7 * $Middle);
$ipData1 = fread($fd, 4);
if (strlen($ipData1) < 4) {
fclose($fd);
return 'System Error';
}
$ip1num = implode('', unpack('L', $ipData1));
if ($ip1num < 0) $ip1num += pow(2, 32);
if ($ip1num > $ipNum) {
$EndNum = $Middle;
continue;
}
$DataSeek = fread($fd, 3);
if (strlen($DataSeek) < 3) {
fclose($fd);
return 'System Error';
}
$DataSeek = implode('', unpack('L', $DataSeek . chr(0)));
fseek($fd, $DataSeek);
$ipData2 = fread($fd, 4);
if (strlen($ipData2) < 4) {
fclose($fd);
return 'System Error';
}
$ip2num = implode('', unpack('L', $ipData2));
if ($ip2num < 0) $ip2num += pow(2, 32);
if ($ip2num < $ipNum) {
if ($Middle == $BeginNum) {
fclose($fd);
return 'Unknown';
}
$BeginNum = $Middle;
}
}
// 读取地址信息
$ipFlag = fread($fd, 1);
if ($ipFlag == chr(1)) {
$ipSeek = fread($fd, 3);
if (strlen($ipSeek) < 3) {
fclose($fd);
return 'System Error';
}
$ipSeek = implode('', unpack('L', $ipSeek . chr(0)));
fseek($fd, $ipSeek);
$ipFlag = fread($fd, 1);
}
// 读取省份和国家
if ($ipFlag == chr(2)) {
$AddrSeek = fread($fd, 3);
if (strlen($AddrSeek) < 3) {
fclose($fd);
return 'System Error';
}
$ipFlag = fread($fd, 1);
if ($ipFlag == chr(2)) {
$AddrSeek2 = fread($fd, 3);
if (strlen($AddrSeek2) < 3) {
fclose($fd);
return 'System Error';
}
$AddrSeek2 = implode('', unpack('L', $AddrSeek2 . chr(0)));
fseek($fd, $AddrSeek2);
} else {
fseek($fd, -1, SEEK_CUR);
}
while (($char = fread($fd, 1)) != chr(0))
$ipAddr2 .= $char;
$AddrSeek = implode('', unpack('L', $AddrSeek . chr(0)));
fseek($fd, $AddrSeek);
while (($char = fread($fd, 1)) != chr(0))
$ipAddr1 .= $char;
function get_ip_location($ip) {
$qqwry = new QQWry(__DIR__ . '/dist/qqwry.dat');
$detail = $qqwry->getDetail($ip);
if ($detail) {
// Assuming dataA is country and dataB is province
return array(
'country' => $detail['dataA']
);
} else {
fseek($fd, -1, SEEK_CUR);
while (($char = fread($fd, 1)) != chr(0))
$ipAddr1 .= $char;
$ipFlag = fread($fd, 1);
if ($ipFlag == chr(2)) {
$AddrSeek2 = fread($fd, 3);
if (strlen($AddrSeek2) < 3) {
fclose($fd);
return 'System Error';
}
$AddrSeek2 = implode('', unpack('L', $AddrSeek2 . chr(0)));
fseek($fd, $AddrSeek2);
} else {
fseek($fd, -1, SEEK_CUR);
}
while (($char = fread($fd, 1)) != chr(0)) {
$ipAddr2 .= $char;
}
return array(
'country' => 'Unknown'
);
}
fclose($fd);
// 处理结果,去除城市和运营商信息
$ipAddr1 = iconv('gbk', 'utf-8//IGNORE', trim($ipAddr1));
$ipAddr2 = iconv('gbk', 'utf-8//IGNORE', trim($ipAddr2));
// 只返回国家和省份
$result = "$ipAddr1 $ipAddr2";
// 清理多余信息
$result = preg_replace('/(.*?)(?:.*)?/', '$1', $result); // 只保留前面的部分
$result = preg_replace('/CZ88.NET|http.*$/i', '', $result);
$result = preg_replace('/\s+/', ' ', $result); // 合并多余空格
$result = trim($result);
if (empty($result)) {
return '可能来自火星';
}
return $result;
}
function convertip($ip) {
echo convertips($ip);
}
}

164
qqwry.php Normal file
View File

@ -0,0 +1,164 @@
<?php
// 数据来源纯真IP数据库 qqwry.dat
// 初始化类new QQWry($fileName)
// 请求方式getDetail($ip)
// 返回格式:
// {
// "beginIP": IP段起始点
// "endIP": IP段结束点
// "dataA": 数据段1
// "dataB": 数据段2
// }
//
// 请求版本getVersion()
// 返回格式YYYYMMDD
class QQWry {
private $fp; // 文件指针
private $firstRecord; // 第一条记录的偏移地址
private $lastRecord; // 最后一条记录的偏移地址
private $recordNum; // 总记录条数
public function __construct($fileName) { // 构造函数
$this->fp = fopen($fileName, 'rb');
$this->firstRecord = $this->read4byte();
$this->lastRecord = $this->read4byte();
$this->recordNum = ($this->lastRecord - $this->firstRecord) / 7; // 每条索引长度为7字节
}
public function __destruct() { // 析构函数
if ($this->fp) {
fclose($this->fp);
}
}
private function read4byte() { // 读取4字节并转为long
return unpack('Vlong', fread($this->fp, 4))['long'];
}
private function read3byte() { // 读取3字节并转为long
return unpack('Vlong', fread($this->fp, 3) . chr(0))['long'];
}
private function readString() { // 读取字符串
$str = '';
$char = fread($this->fp, 1);
while (ord($char) != 0) { // 读到二进制0结束
$str .= $char;
$char = fread($this->fp, 1);
}
return $str;
}
private function zipIP($ip) { // IP地址转为数字
$ip_arr = explode('.', $ip);
$tmp = (16777216 * intval($ip_arr[0])) + (65536 * intval($ip_arr[1])) + (256 * intval($ip_arr[2])) + intval($ip_arr[3]);
return pack('N', intval($tmp)); // 32位无符号大端序长整型
}
private function unzipIP($ip) { // 数字转为IP地址
return long2ip($ip);
}
public function getVersion() { // 获取当前数据库的版本
fseek($this->fp, $this->lastRecord + 4);
$tmp = $this->getRecord($this->read3byte())['B'];
return substr($tmp, 0, 4) . substr($tmp, 7, 2) . substr($tmp, 12, 2);
}
public function getDetail($ip) { // 获取IP地址区段及所在位置
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { // 判断是否为IPv4地址
return null;
}
fseek($this->fp, $this->searchRecord($ip)); // 跳转到对应IP记录的位置
$detail['beginIP'] = $this->unzipIP($this->read4byte()); // 目标IP所在网段的起始IP
$offset = $this->read3byte(); // 索引后3字节为对应记录的偏移量
fseek($this->fp, $offset);
$detail['endIP'] = $this->unzipIP($this->read4byte()); // 目标IP所在网段的结束IP
$tmp = $this->getRecord($offset); // 获取记录的dataA与dataB
$detail['dataA'] = $tmp['A'];
$detail['dataB'] = $tmp['B'];
if ($detail['beginIP'] == '255.255.255.0') { // 去除附加信息
$detail['dataA'] = 'IANA';
$detail['dataB'] = '保留地址';
}
if ($detail['dataA'] == ' CZ88.NET' || $detail['dataA'] == '纯真网络') {
$detail['dataA'] = '';
}
if ($detail['dataB'] == ' CZ88.NET') {
$detail['dataB'] = '';
}
return $detail;
}
private function searchRecord($ip) { // 根据IP地址获取索引的绝对偏移量
$ip = $this->zipIP($ip); // 转为数字以比较大小
$down = 0;
$up = $this->recordNum;
while ($down <= $up) { // 二分法查找
$mid = floor(($down + $up) / 2); // 计算二分点
fseek($this->fp, $this->firstRecord + $mid * 7);
$beginip = strrev(fread($this->fp, 4)); // 获取二分区域的下边界
if ($ip < $beginip) { // 目标IP在二分区域以下
$up = $mid - 1; // 缩小搜索的上边界
} else {
fseek($this->fp, $this->read3byte());
$endip = strrev(fread($this->fp, 4)); // 获取二分区域的上边界
if ($ip > $endip) { // 目标IP在二分区域以上
$down = $mid + 1; // 缩小搜索的下边界
} else { // 目标IP在二分区域内
return $this->firstRecord + $mid * 7; // 返回索引的偏移量
}
}
}
return $this->lastRecord; // 无法找到对应索引,返回最后一条记录的偏移量
}
private function getRecord($offset) { // 读取IP记录的数据
fseek($this->fp, $offset + 4);
$flag = ord(fread($this->fp, 1));
if ($flag == 1) { // dataA与dataB均重定向
$offset = $this->read3byte(); // 重定向偏移
fseek($this->fp, $offset);
if (ord(fread($this->fp, 1)) == 2) { // dataA再次重定向
fseek($this->fp, $this->read3byte());
$data['A'] = $this->readString();
fseek($this->fp, $offset + 4);
$data['B'] = $this->getDataB();
} else { // dataA无重定向
fseek($this->fp, -1, SEEK_CUR); // 文件指针回退1字节
$data['A'] = $this->readString();
$data['B'] = $this->getDataB();
}
} else if ($flag == 2) { // dataA重定向
fseek($this->fp, $this->read3byte());
$data['A'] = $this->readString();
fseek($this->fp, $offset + 8); // IP占4字节, 重定向标志占1字节, dataA指针占3字节
$data['B'] = $this->getDataB();
} else { // dataA无重定向
fseek($this->fp, -1, SEEK_CUR); // 文件指针回退1字节
$data['A'] = $this->readString();
$data['B'] = $this->getDataB();
}
$data['A'] = iconv("GBK", "UTF-8", $data['A']); // GBK -> UTF-8
$data['B'] = iconv("GBK", "UTF-8", $data['B']);
return $data;
}
private function getDataB() { // 从fp指定偏移获取dataB
$flag = ord(fread($this->fp, 1));
if ($flag == 0) { // dataB无信息
return '';
} else if ($flag == 1 || $flag == 2) { // dataB重定向
fseek($this->fp, $this->read3byte());
return $this->readString();
} else { // dataB无重定向
fseek($this->fp, -1, SEEK_CUR); // 文件指针回退1字节
return $this->readString();
}
}
}
?>