• 899阅读
  • 42回复

为你守候 温馨家园 即日起开始拥有自己的IP归属信息表 [复制链接] [手机版]

上一主题 下一主题
离线zhangjingyu

只看该作者 30楼 发表于: 2026-06-27 20:33:52 星期六  点亮(0)
$rs = $db->get_one("SELECT * FROM pw_ipdatabase ...");

    if($rs && !empty($rs['ipfrom'])){
        return $rs['ipfrom']; 这个判断是不是有问题?数据表里ipfrom字段为空,导致读取出来的归属地也是空的,不应该是这样的啊

你这个问题问得很细,说明你认真看了代码。直接回答你的判断:从逻辑上讲,这个判断本身没有语法错误,但从业务上讲,它确实“有问题”,而且很可能就是导致你看到空归属地的根本原因。

下面我分两层给你拆解:

1. 为什么判断逻辑“有问题”?(业务层面)
你的困惑在于:“表里是空的,查出来也应该是空的,但代码应该返回一个默认值(比如‘未知’),而不是真的返回空啊。”

问题就出在 !empty($rs['ipfrom']) 这个条件上:

当数据库里 ipfrom 字段真的是 NULL 或者空字符串 '' 时,empty() 函数会判定为 true。

因此,if($rs && !empty($rs['ipfrom'])) 条件不成立,代码不会执行 return $rs['ipfrom']。

如果这个函数后面没有其他 return 语句(比如 return '未知地区'),那么该函数最终会隐式返回 null。

结论:这个代码的设计初衷是“查到了非空数据就返回,查不到或为空就继续往下走(可能给默认值)”。如果后续没有兜底逻辑,那么空字段查出来就是 null,而不是你期望的“未知”。

2. 除了逻辑,这行代码还有两个“硬伤”(技术层面)
如果你想要更健壮,这个写法还有两个明显的坑:

empty() 会吞掉合法的 IP 归属地:如果某个 IP 归属地恰好叫 '0'(比如某个城市代号),empty('0') 会返回 true,导致代码认为它是空的而不返回。虽然少见,但这是经典 Bug。

SQL 查询写法不规范:"SELECT * FROM pw_ipdatabase ..." 如果没加 LIMIT 1,虽然 get_one 方法可能只取一条,但在高并发下可能增加数据库负担。

3. 你应该怎么改?
如果是你维护这段代码,建议按以下标准写法修复,彻底解决“空值返回空”的问题:

php
// 方案一(推荐):直接判断是否存在,不依赖 empty
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE ... LIMIT 1");

// 使用 isset 判断是否存在该键,且严格判断不为空字符串和 null
if ($rs && isset($rs['ipfrom']) && $rs['ipfrom'] !== '' && $rs['ipfrom'] !== null) {
    return $rs['ipfrom'];
}

// 兜底返回:保证任何时候调用都有值
return '未知地区';
或者更简洁的写法(PHP 7+):

php
// 使用 null 合并运算符,但前提是数据库字段默认值设为 NULL 而不是空字符串
if ($rs && !is_null($rs['ipfrom'])) {
    return $rs['ipfrom'];
}
return '未知地区';
总结
你现在的代码逻辑上是在“忽略”空值,而不是“返回”空值。所以当表里是空的时候,它不会报错,但会返回 null,导致前端或上层应用显示空白。

解决核心:不要依赖 empty() 来判断数据库返回的字符串,改用 isset() 配合 !== '',并且务必加上 else 返回默认值。

如果你能贴出这个函数后面几行的代码,我可以帮你确认是不是因为缺少 else 导致最终返回了 null。如果需要我帮你重写整个查询函数,也可以告诉我。 😊

$rs=$db->get_one("SELECT * FROM pw_ipdatabase where onlineip like '$kw' order by id desc limit 1");//倒序查询最新的一个
    $guishu=$rs['ipfrom'];
        if($rs && !empty($guishu)){
        return $guishu;
        }数据表里的归属地如果是空的,不应该return,而是应该继续执行,去获取对应的归属地
啊,这下我完全明白你的意思了!你说得非常对,而且我发现了一个更隐蔽的严重问题!

你的理解完全正确
你的业务逻辑是:

先从数据库查归属地

如果查到非空的归属地,直接返回

如果查到的是空或者没查到,应该继续往下执行(比如调用第三方API获取)

但现在的问题是:当 ipfrom 字段为空字符串 '' 时,!empty($guishu) 为 false,代码确实不会 return,会继续执行。这本身没问题。

但真正的坑在这里:SQL 查询逻辑有致命缺陷!
看你这行 SQL:

php
$rs = $db->get_one("SELECT * FROM pw_ipdatabase where onlineip like '$kw' order by id desc limit 1");
问题1:like 匹配可能导致查出来的根本不是这条IP
假设你的IP是 192.168.1.100,$kw 可能是 '192.168.1.100%' 或者 '%192.168.1.100%'。

如果 $kw = '192.168.1.100%',会匹配到 192.168.1.100、192.168.1.1000、192.168.1.10001 等

如果 $kw = '%192.168.1.100%',匹配范围就更大了

结果:可能查到的是 192.168.1.1000 的归属地,而 192.168.1.100 自己的归属地反而是空的。查出来的是别人的数据,当然就是空的!

问题2:order by id desc limit 1 取最新一条
如果同一个IP段有多条记录,取 id 最大的那条。但这不能保证是最精确匹配的那条。

正确的做法应该是
方案一:精确匹配(推荐)
php
// 假设 $ip 是完整的IP地址
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip = '$ip' LIMIT 1");

if ($rs && $rs['ipfrom'] !== '' && $rs['ipfrom'] !== null) {
    return $rs['ipfrom'];
}

// 如果精确匹配没有,再尝试模糊匹配
// ... 继续执行获取归属地的逻辑
方案二:如果必须用 like,要确保匹配精度
php
// 匹配IP段(比如 192.168.1.%)
$kw = addslashes($ip) . '%'; // 只匹配以该IP开头的
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY LENGTH(onlineip) DESC, id DESC LIMIT 1");
// 按长度排序,确保最精确的匹配排在前面
完整建议的代码逻辑
php
// 1. 先精确查询
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip = '$ip' LIMIT 1");

// 2. 如果有数据且归属地不为空,直接返回
if ($rs && isset($rs['ipfrom']) && $rs['ipfrom'] !== '' && $rs['ipfrom'] !== null) {
    return $rs['ipfrom'];
}

// 3. 如果没有查到,或者查到了但是空的,继续执行获取归属地的逻辑
// 比如调用第三方API
$guishu = getIpFromThirdParty($ip);

// 4. 拿到归属地后,插入数据库,供下次使用
if ($guishu) {
    $db->query("INSERT INTO pw_ipdatabase (onlineip, ipfrom) VALUES ('$ip', '$guishu')");
    return $guishu;
}

// 5. 最终兜底
return '未知地区';
总结
你最初的问题"判断是不是有问题"——判断本身没问题,但SQL查询方式导致查出来的数据可能不准确。

核心矛盾:

你用 like 模糊匹配,可能查到了错误的记录

那条错误的记录的 ipfrom 恰好是空的

所以返回了空,但实际上你想要的那条IP的归属地可能在其他记录里

解决方案:优先使用 = 精确匹配,只有精确匹配查不到时才用 like 模糊匹配。

你现在明白为什么数据表里归属地是空,但查出来的也是空了吧?因为查的根本就不是同一条记录!需要我帮你写一个完整的IP查询函数吗?包括缓存、第三方API调用和数据库存储的完整流程。😊
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 31楼 发表于: 2026-06-27 20:35:09 星期六  点亮(0)
ip的查询应该是没有问题的,前面的代码还有    $newip = preg_replace('/\.\d+$/', '', $onlineip);//去掉ip地址最后面的点和数字
    $kw=$newip.'%';//构造查询数据表关键字,百分号为通配符

啊!看到这段代码我就全明白了!你的IP查询逻辑设计得很巧妙,是典型的IP段匹配方案。现在问题就清晰了:

你的设计思路(完全正确)
php
$newip = preg_replace('/\.\d+$/', '', $onlineip); // 192.168.1.100 → 192.168.1
$kw = $newip.'%'; // 构造为 '192.168.1.%'
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");
你是想:

用 192.168.1.% 去匹配 192.168.1.0、192.168.1.1、192.168.1.255 等整个C段

取最新的一条记录作为该IP段的归属地
…………
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 32楼 发表于: 2026-06-27 20:39:17 星期六  点亮(0)
if($rs && !empty($rs['ipfrom'])){
        return $rs['ipfrom']; 这个判断还是很疑惑,如果归属地是空的,这个判断应该是不成立的的,按说不应该返回空的归属地啊

你说得太对了!我重新仔细分析一下这个判断逻辑:

你的理解完全正确!
php
if($rs && !empty($rs['ipfrom'])){
    return $rs['ipfrom'];
}
当 $rs['ipfrom'] 为空字符串 '' 时:

empty('') 返回 true

!empty('') 返回 false

所以 if 条件不成立,不会执行 return

所以理论上,这个判断确实不应该返回空的归属地。

那为什么你看到的却是返回了空?
既然判断逻辑没问题,那只有两种可能:

可能性1:你看到的"空"不是从这行代码返回的
检查一下这个函数后面还有没有其他 return:

php
function getIpGuishu($onlineip) {
    // ... 前面的代码
    $rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");
    
    if($rs && !empty($rs['ipfrom'])){
        return $rs['ipfrom'];  // ← 这里没返回
    }
    
    // 继续执行获取归属地的逻辑
    $guishu = getFromThirdParty($onlineip);
    
    if($guishu) {
        return $guishu;  // ← 可能这里返回了空?
    }
    
    // 如果所有方法都失败,最后可能返回了空
    return '';  // ← 或者这里直接返回空字符串?
}
可能性2:数据表里存的不是"空",而是其他被 empty() 判定为真的值
empty() 会判定以下值为 true:

"" (空字符串)

0 (整数0)

"0" (字符串0)

NULL

false

[] (空数组)

如果数据表里存的是 "0"(字符串0),比如某个地区代码是0:

empty("0") 返回 true

!empty("0") 返回 false

不会执行 return,但后续逻辑可能返回了 "0"

快速验证方法
在你代码里加一行调试:

php
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

// 调试代码开始
error_log("=== 调试信息 ===");
error_log("查询结果: " . print_r($rs, true));
error_log("ipfrom 值: '" . $rs['ipfrom'] . "'");
error_log("ipfrom 类型: " . gettype($rs['ipfrom']));
error_log("empty判断: " . (empty($rs['ipfrom']) ? 'true' : 'false'));
// 调试代码结束

$guishu = $rs['ipfrom'];
if($rs && !empty($guishu)){
    return $guishu;
}
然后查看 error_log,看看 ipfrom 到底是什么值。

我怀疑的真实情况
根据你之前的描述,我猜测最可能的是这个流程:

php
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

$guishu = $rs['ipfrom'];  // ← 这里赋值了空字符串

if($rs && !empty($guishu)){
    return $guishu;  // ← 条件不成立,跳过
}

// 下面继续执行获取归属地的代码
// ... 可能调用第三方API,但API失败了,返回了空
// ... 或者没有任何 else/默认值,函数最后隐式返回了 null

// 最终返回了空/null给调用方
建议的完整写法
php
function getIpGuishu($onlineip) {
    // 1. 查询数据库
    $newip = preg_replace('/\.\d+$/', '', $onlineip);
    $kw = $newip . '%';
    $rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");
    
    // 2. 如果查到了非空的归属地,直接返回
    if ($rs && !empty($rs['ipfrom'])) {
        return $rs['ipfrom'];
    }
    
    // 3. 否则,调用第三方API获取
    $guishu = getFromThirdParty($onlineip);
    
    // 4. 如果获取到了,存入数据库并返回
    if (!empty($guishu)) {
        // 判断是更新还是插入
        if ($rs) {
            // 更新已有记录
            $db->query("UPDATE pw_ipdatabase SET ipfrom = '$guishu' WHERE id = {$rs['id']}");
        } else {
            // 插入新记录
            $db->query("INSERT INTO pw_ipdatabase (onlineip, ipfrom) VALUES ('$onlineip', '$guishu')");
        }
        return $guishu;
    }
    
    // 5. 实在获取不到,返回默认值(这里是关键!)
    return '未知地区';  // ← 绝对不能返回空!
}
总结
你的困惑是对的,这个判断确实不会返回空归属地。

如果最终拿到了空值,一定是后续逻辑返回的。可能是:

第三方API调用失败返回了空

函数最后没有 return 默认值,隐式返回了 null

其他地方返回了空

建议你加调试日志,看看 $guishu 到底是在哪里变成了空。

你可以把完整的函数代码发给我,我帮你分析具体是哪个环节出了问题。😊
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 33楼 发表于: 2026-06-27 20:40:30 星期六  点亮(0)
…………
总结
你的 if($rs && !empty($guishu)) 判断完全没有问题。

真正的问题是:

从第三方获取归属地失败时,没有给默认值,导致空值入库

空值入库后,下次查询优先取到这条空记录(因为 ORDER BY id DESC)

代码看到是空记录,又去调用第三方,但可能还是失败,形成恶性循环

解决方案的核心:无论如何都要给 $guishudi 一个默认值(比如 '未知地区'),绝对不能允许空值或 null 插入数据库。

需要我帮你写完整的 utf8ToNumericEntities 函数吗?或者帮你优化整个IP归属地获取类?😊
无论如何都要给 $guishudi 一个默认值(比如 '未知地区')那下次查询到近似的ip,归属地不就是返回“未知地区”吗
你说得太对了! 这是个致命问题!

如果给默认值 '未知地区' 入库,下次查询同IP段时:

数据库查到了记录($rs 有值)

$rs['ipfrom'] = '未知地区'

!empty('未知地区') 为 true

直接返回 '未知地区',不会再尝试获取真实归属地!

这样就会永远显示"未知地区",完全失去了自动获取的意义!
…………
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 34楼 发表于: 2026-06-27 20:43:04 星期六  点亮(0)
我当前在线ip是112.38.51.125,数据表里有一条112.38.51.118的空归属地记录。然而,// 2. 如果有记录但归属地是空字符串 ''(获取失败过),直接调用 pconline
//    不再尝试 ip.cn(因为可能已经失败过)
if ($rs && $rs['ipfrom'] === '') {
    // 直接尝试 pconline
    $guishudi = getLocationFromPconline($onlineip);
    if (!empty($guishudi)) {
        // 更新数据库
        $db->query("UPDATE pw_ipdatabase SET ipfrom='$guishudi', source='pconline.com.cn' WHERE id={$rs['id']}");
        return $guishudi;
    }
    // pconline 也失败,保持空字符串,不返回默认值
    return ''; // 或者返回 '未知',但下次还会尝试获取
}这段代码并没有更新这条记录。尝试屏蔽// 1. 如果有记录且归属地不为 NULL 且不为空字符串,直接返回
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') {
    return $rs['ipfrom'];
}这三行代码,结果是新添加了一条112.38.51.125的记录。帮我分析分析怎么回事
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 35楼 发表于: 2026-06-27 20:52:27 星期六  点亮(0)
非常好的测试!你这个问题一下子就暴露了SQL查询的致命问题,我找到根本原因了!

问题根源:LIKE 模糊匹配的"陷阱"
你的SQL是:

sql
SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '112.38.51.%' ORDER BY id DESC LIMIT 1
你的数据表里有一条:112.38.51.118(归属地为空)

当你查询 112.38.51.125 时:

$newip = preg_replace('/\.\d+$/', '', '112.38.51.125') → '112.38.51'

$kw = '112.38.51.%'

SQL 会匹配到:112.38.51.118、112.38.51.125(如果有)等所有以 112.38.51. 开头的记录

ORDER BY id DESC LIMIT 1 → 取 id 最大的那条

关键问题:ORDER BY id DESC 取到的是哪条?
你的数据库里:

112.38.51.118(空归属地),假设 id = 100

查询 112.38.51.125 时,匹配到了 112.38.51.118

ORDER BY id DESC 取 id 最大的那条

如果 112.38.51.118 的 id 是最大的,就取到了这条空记录!

你的测试证明了什么?
测试1:没有屏蔽 if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '')
php
// 查询结果:$rs 有值(112.38.51.118),但 ipfrom = ''
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') {
    return $rs['ipfrom'];  // 条件不成立,跳过
}

// 进入空字符串判断
if ($rs && $rs['ipfrom'] === '') {
    // 应该进入这里才对!
    // 但你说没有更新,说明根本没进入这个 if
}
为什么没进入? 唯一的可能是 $rs['ipfrom'] 不是空字符串 '',而是 NULL!

测试2:屏蔽了返回判断,直接插入新记录
php
// 跳过了所有判断,直接执行插入
$db->query("INSERT INTO pw_ipdatabase SET ... onlineip='112.38.51.125', ipfrom='$guishudi'");
结果插入了新记录,说明:

查询确实匹配到了 112.38.51.118($rs 有值)

但因为某种原因,$rs['ipfrom'] 既不是非空字符串,也不是 ''

所以跳过了两个 if,直接执行到后面的插入逻辑

真正的问题:$rs['ipfrom'] 到底是什么?
结合你的测试,我强烈怀疑:

数据表里 ipfrom 字段的值不是空字符串 '',而是 NULL!

php
// 如果 ipfrom = NULL
$rs['ipfrom'] = null;

// 判断1
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') {
    // null !== null → false,不进入
}

// 判断2
if ($rs && $rs['ipfrom'] === '') {
    // null === '' → false,不进入
}

// 两个 if 都没进,继续执行后面的插入逻辑!
验证方法
执行这个SQL看看:

sql
SELECT id, onlineip, ipfrom,
       CASE WHEN ipfrom IS NULL THEN 'NULL'
            WHEN ipfrom = '' THEN '空字符串'
            ELSE ipfrom
       END as 实际值
FROM pw_ipdatabase
WHERE onlineip LIKE '112.38.51.%'
ORDER BY id DESC;
我敢肯定,112.38.51.118 的 ipfrom 字段是 NULL,不是空字符串!

为什么是 NULL?
很可能你的数据表定义是:

sql
CREATE TABLE pw_ipdatabase (
    ...
    ipfrom varchar(255) DEFAULT NULL,  -- 默认是 NULL
    ...
);
插入数据时,如果没指定 ipfrom 的值,默认就是 NULL。

正确的修复方案
php
// 1. 如果查到有效归属地(非NULL且非空),直接返回
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') {
    return $rs['ipfrom'];
}

// 2. 如果查到了记录,但归属地是空的(NULL 或 '')
if ($rs && ($rs['ipfrom'] === null || $rs['ipfrom'] === '')) {
    // 直接尝试 pconline
    $guishudi = getLocationFromPconline($onlineip);
    if (!empty($guishudi)) {
        // 更新数据库
        $db->query("UPDATE pw_ipdatabase SET ipfrom='$guishudi', source='pconline.com.cn' WHERE id={$rs['id']}");
        return $guishudi;
    }
    // pconline 也失败,保持原样(NULL),不返回默认值
    return '';
}

// 3. 没有记录($rs === false),完整获取流程
// ... 调用 ip.cn、pconline 等
建议:统一空值表示
为了避免混淆,建议统一用 NULL 表示"未获取":

sql
-- 修改表结构,设置默认值为 NULL
ALTER TABLE pw_ipdatabase MODIFY ipfrom varchar(255) DEFAULT NULL;

-- 更新所有空字符串为 NULL
UPDATE pw_ipdatabase SET ipfrom = NULL WHERE ipfrom = '';
然后代码统一判断:

php
// 有效数据:非 NULL 且非空字符串
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') {
    return $rs['ipfrom'];
}

// 无效数据:NULL 或 空字符串
if ($rs && ($rs['ipfrom'] === null || $rs['ipfrom'] === '')) {
    // 尝试重新获取
}
总结
你的测试完美地暴露了问题:

数据表里存的是 NULL,不是空字符串 ''

=== '' 判断不到 NULL,所以没进入更新逻辑

跳过了所有判断,直接执行了插入新记录

解决方法:把 === '' 改成 === null || === '',或者直接用 empty()(因为 empty(null) 和 empty('') 都返回 true)。

快查一下数据库,我赌 5 毛钱那条记录的 ipfrom 是 NULL!😄
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 36楼 发表于: 2026-06-27 20:53:41 星期六  点亮(0)
`ipfrom` mediumtext NOT NULL,是这个原因吗?

找到根本原因了! 就是这个 NOT NULL 导致的!

问题分析
sql
`ipfrom` mediumtext NOT NULL
这个字段定义:

NOT NULL:不允许插入 NULL 值

没有默认值:如果插入时没指定值,MySQL 会使用空字符串 '' 作为默认值(对于 NOT NULL 的字符串字段)

那为什么你的 === '' 判断还是没生效?
有两种可能:

可能性1:你之前插入时用了 NULL,但被转换了
因为字段是 NOT NULL,当你尝试插入 NULL 时,MySQL 会:

严格模式:报错

非严格模式:自动转换成空字符串 ''

所以你的 112.38.51.118 记录的 ipfrom 确实是空字符串 ''。

可能性2:问题出在查询结果上(最可能!)
看你的查询逻辑:

php
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");
get_one() 方法可能返回的不是数组,而是其他格式!

比如:

如果 get_one() 返回的是对象(stdClass),那么 $rs['ipfrom'] 是错误的访问方式

应该用 $rs->ipfrom

这样 $rs['ipfrom'] 可能返回 NULL,导致 === '' 判断失败!

验证方法
php
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

// 调试输出
var_dump($rs);  // 看看返回的是什么类型
var_dump($rs['ipfrom']);  // 看看值是什么
var_dump($rs['ipfrom'] === '');  // 看看判断结果
var_dump($rs['ipfrom'] === null);  // 看看是不是NULL
var_dump(empty($rs['ipfrom']));  // 看看empty判断
真正的修复方案
不管 get_one() 返回什么格式,统一处理:

php
// 1. 查询数据库
$newip = preg_replace('/\.\d+$/', '', $onlineip);
$kw = $newip.'%';
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

// 2. 关键:获取 ipfrom 的值(兼容数组和对象)
$ipfrom = null;
if ($rs) {
    if (is_array($rs)) {
        $ipfrom = $rs['ipfrom'] ?? null;
    } else if (is_object($rs)) {
        $ipfrom = $rs->ipfrom ?? null;
    }
}

// 3. 如果有记录且归属地有效(非空),直接返回
if ($rs && !empty($ipfrom)) {
    return $ipfrom;
}

// 4. 如果有记录但归属地为空(NULL 或 空字符串)
if ($rs && (empty($ipfrom) || $ipfrom === '')) {
    // 直接尝试 pconline
    $guishudi = getLocationFromPconline($onlineip);
    if (!empty($guishudi)) {
        // 更新数据库(注意:字段是 NOT NULL,所以用空字符串作为失败标记)
        $db->query("UPDATE pw_ipdatabase SET ipfrom='$guishudi', source='pconline.com.cn' WHERE id={$rs['id']}");
        return $guishudi;
    }
    // 获取失败,更新为空字符串(因为 NOT NULL)
    $db->query("UPDATE pw_ipdatabase SET ipfrom='', source='pconline.com.cn' WHERE id={$rs['id']}");
    return '';
}

// 5. 没有记录,完整获取流程
// ... 调用 ip.cn、pconline 等

// 6. 获取成功后插入(因为 NOT NULL,必须给值)
if (!empty($guishudi)) {
    $db->query("INSERT INTO pw_ipdatabase SET ... ipfrom='$guishudi'");
} else {
    // 获取失败,也要插入(因为 NOT NULL)
    $db->query("INSERT INTO pw_ipdatabase SET ... ipfrom=''");
}
为什么 === '' 没生效?
结合你的测试,最可能的原因是:

get_one() 返回的是对象,你用 $rs['ipfrom'] 访问导致返回 NULL

php
// 假设 get_one() 返回对象
$rs = new stdClass();
$rs->ipfrom = '';  // 实际值是空字符串

// 错误访问方式
var_dump($rs['ipfrom']);  // 返回 NULL(因为对象不支持数组访问)

// 正确访问方式
var_dump($rs->ipfrom);  // 返回 ''
所以你的 === '' 判断,实际上是在判断 NULL === '',结果当然是 false!

快速验证
在你的代码里加上这几行调试:

php
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

// 调试开始
if ($rs) {
    error_log("=== 调试信息 ===");
    error_log("rs 类型: " . gettype($rs));
    error_log("rs 内容: " . print_r($rs, true));
    
    if (is_array($rs)) {
        error_log("ipfrom: '" . ($rs['ipfrom'] ?? 'NULL') . "'");
        error_log("=== 判断: " . ($rs['ipfrom'] === '' ? 'true' : 'false'));
    } else if (is_object($rs)) {
        error_log("ipfrom: '" . ($rs->ipfrom ?? 'NULL') . "'");
        error_log("=== 判断: " . ($rs->ipfrom === '' ? 'true' : 'false'));
    }
}
// 调试结束
然后查看错误日志,结果一目了然!

最终建议
搞清楚 get_one() 的返回类型(数组还是对象)

用正确的方式访问 ipfrom

用 empty() 统一判断(因为 empty('') 和 empty(null) 都返回 true)

最简单的修复:

php
if ($rs && !empty($rs['ipfrom'])) {  // 如果 $rs 是数组
    return $rs['ipfrom'];
}

// 或者
if ($rs && !empty($rs->ipfrom)) {  // 如果 $rs 是对象
    return $rs->ipfrom;
}
empty() 可以同时处理 NULL、空字符串、0、false 等情况,比 === '' 更宽容。

快试试吧!我赌这次一定能解决!😊
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 37楼 发表于: 2026-06-27 20:54:35 星期六  点亮(0)
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

// 调试输出
var_dump($rs);  // 看看返回的是什么类型
var_dump($rs['ipfrom']);  // 看看值是什么
var_dump($rs['ipfrom'] === '');  // 看看判断结果
var_dump($rs['ipfrom'] === null);  // 看看是不是NULL
var_dump(empty($rs['ipfrom']));  // 看看empty判断  这几行代码运行后的结果是:bool(false) NULL bool(false) bool(true) bool(true)

破案了! 问题非常明确!

调试结果分析
php
var_dump($rs);                    // bool(false)  ← 查询没找到任何记录!
var_dump($rs['ipfrom']);          // NULL         ← 因为 $rs 是 false,所以访问失败
var_dump($rs['ipfrom'] === '');   // bool(false)  ← 比较失败
var_dump($rs['ipfrom'] === null); // bool(true)   ← NULL === null 为 true
var_dump(empty($rs['ipfrom']));   // bool(true)   ← 空值判断为 true
关键发现:$rs = bool(false),查询没有返回任何记录!

为什么查不到 112.38.51.118?
你的SQL是:

sql
SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '112.38.51.%' ORDER BY id DESC LIMIT 1
理论上应该匹配到 112.38.51.118,但查不到,可能的原因:

可能性1:数据库里根本没有以 112.38.51. 开头的记录
你说数据表里有 112.38.51.118,但可能:

这条记录被删除了

或者 onlineip 字段存的是其他格式(比如 112.38.51.118 带空格?)

可能性2:$kw 的值不对
php
$newip = preg_replace('/\.\d+$/', '', $onlineip);
// $onlineip = '112.38.51.125'
// $newip = '112.38.51'
// $kw = '112.38.51.%'
这个逻辑没问题。

但如果你当前IP不是 112.38.51.125,而是其他IP,那 $kw 就是其他值。

可能性3:字符集或编码问题
$kw 可能有隐藏字符,或者数据库里的 onlineip 有特殊字符。

快速验证
直接在数据库里执行:

sql
-- 1. 看看 112.38.51.118 到底在不在
SELECT * FROM pw_ipdatabase WHERE onlineip = '112.38.51.118';

-- 2. 看看所有 112.38.51 开头的记录
SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '112.38.51.%';

-- 3. 看看这条记录的 id 是多少
SELECT id, onlineip, ipfrom FROM pw_ipdatabase WHERE onlineip = '112.38.51.118';

-- 4. 检查是否有空格或其他隐藏字符
SELECT id, onlineip, LENGTH(onlineip) as len, CHAR_LENGTH(onlineip) as char_len
FROM pw_ipdatabase WHERE onlineip LIKE '%112.38.51.118%';
为什么屏蔽判断后能插入新记录?
因为 $rs = false,所以所有的 if($rs && ...) 判断都跳过了,直接执行到后面的插入逻辑:

php
// 所有判断都失败
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') { }  // false,跳过
if ($rs && $rs['ipfrom'] === '') { }  // false,跳过

// 继续执行
// ... 获取归属地
// ... 插入新记录
$db->query("INSERT INTO pw_ipdatabase SET ... onlineip='112.38.51.125' ...");
问题的真正原因
数据表里 112.38.51.118 可能根本不在 pw_ipdatabase 表中!

你说"数据表里有一条112.38.51.118的空归属地记录",但:

可能这条记录在另一个表里

可能被误删了

可能 onlineip 字段值不是 112.38.51.118,而是 112.38.51.118(带空格)或其他格式

完整的修复方案
既然 $rs = false,说明数据库里没有匹配的记录,那就应该直接获取归属地并插入。

但要注意:插入前先检查是否已存在该IP,防止重复插入。

php
// 1. 查询数据库(加上精确匹配优先)
$newip = preg_replace('/\.\d+$/', '', $onlineip);
$kw = $newip . '%';

// 先精确查询(防止重复)
$rs_exact = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip = '$onlineip' LIMIT 1");
if ($rs_exact && !empty($rs_exact['ipfrom'])) {
    return $rs_exact['ipfrom'];
}

// 再模糊查询
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

if ($rs) {
    // 有记录,检查归属地
    if (!empty($rs['ipfrom'])) {
        return $rs['ipfrom'];
    }
    
    // 有记录但归属地为空,尝试更新
    $guishudi = getLocationFromPconline($onlineip);
    if (!empty($guishudi)) {
        $db->query("UPDATE pw_ipdatabase SET ipfrom='$guishudi', source='pconline.com.cn' WHERE id={$rs['id']}");
        return $guishudi;
    }
    // 更新失败标记
    $db->query("UPDATE pw_ipdatabase SET ipfrom='' WHERE id={$rs['id']}");
    return '';
}

// 2. 没有记录,获取归属地
$guishudi = '';
// 尝试 ip.cn
// ...

// 尝试 pconline
if (empty($guishudi)) {
    $guishudi = getLocationFromPconline($onlineip);
}

// 3. 插入数据库(不管是否获取成功,都插入)
if ($guishudi) {
    $db->query("INSERT INTO pw_ipdatabase SET onlineip='$onlineip', ipfrom='$guishudi', source='$source'");
} else {
    // 获取失败,插入空记录(下次可以重新尝试)
    $db->query("INSERT INTO pw_ipdatabase SET onlineip='$onlineip', ipfrom=''");
}

return $guishudi;
总结
你的测试结果清楚地表明:

$rs = false:查询没有找到任何匹配的记录

所以 112.38.51.118 这条记录根本不在表里,或者不在匹配范围内

屏蔽判断后能插入新记录,说明插入逻辑没问题

解决方案:

先去数据库确认 112.38.51.118 是否真的存在

加上精确匹配查询,防止重复插入

优化代码逻辑,让 $rs = false 时也能正确处理

快去数据库查一下 112.38.51.118 到底在不在!😊
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 38楼 发表于: 2026-06-27 20:56:13 星期六  点亮(0)
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '112.38.51.%';");

// 调试输出
var_dump($rs);  // 看看返回的是什么类型
var_dump($rs['id']);  // 看看值是什么
var_dump($rs['ipfrom']);  // 看看值是什么
var_dump($rs['ipfrom'] === '');  // 看看判断结果
var_dump($rs['ipfrom'] === null);  // 看看是不是NULL
var_dump(empty($rs['ipfrom']));  // 看看empty判断  运行结果:array(9) { ["id"]=> string(2) "24" ["uid"]=> string(1) "2" ["name"]=> string(11) "zhangjingyu" ["tid"]=> string(4) "3963" ["timestamp"]=> string(10) "1779800806" ["date"]=> string(19) "2026-05-26 21:06:46" ["onlineip"]=> string(13) "112.38.51.118" ["ipfrom"]=> string(1) " " ["source"]=> string(0) "" } string(2) "24" string(1) " " bool(false) bool(false) bool(false)
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!
离线zhangjingyu

只看该作者 39楼 发表于: 2026-06-27 20:57:03 星期六  点亮(0)
终于找到真相了! 这是一个非常经典且隐蔽的Bug!

问题真相
看这个关键输出:

["ipfrom"]=> string(1) " "   // 注意:不是空字符串,而是一个空格!
string(1) " " 表示:这是一个包含一个空格的字符串,不是空字符串 ""!


为什么所有判断都失败了?
php
$rs['ipfrom'] = " ";  // 一个空格

var_dump($rs['ipfrom'] === '');   // false(空格 !== 空字符串)
var_dump($rs['ipfrom'] === null); // false(空格 !== null)
var_dump(empty($rs['ipfrom']));   // false(空格不是空值!)
因为 empty(" ") 返回 false(字符串包含空格,不算空值)!

这导致什么后果?
php
// 判断1:如果有记录且归属地不为NULL且不为空字符串
if ($rs && $rs['ipfrom'] !== null && $rs['ipfrom'] !== '') {
    return $rs['ipfrom'];  // ← 条件成立!返回 " "(一个空格)
}

// 因为空格不是空字符串,也不是NULL,所以这个判断为true
// 直接返回了一个空格!
所以你的页面显示空白,但实际上返回的是一个空格字符!

这个空格怎么来的?
最可能的原因
在之前的插入或更新时,$guishudi 变量包含了空格:

php
// 比如从网页解析时
$str4 = strip_tags($str3[1]);
$str4 = str_replace(' ', '', $str4);  // 你这里删除了空格
$str4 = trim($str4);  // 但可能没有执行 trim()

// 如果 $str4 原本是 " "(一个空格)
// str_replace(' ', '', $str4) → ""(空字符串)
// 但如果代码顺序不对,可能没执行到
或者数据库插入时
php
$db->query("INSERT INTO pw_ipdatabase SET ... ipfrom='$guishudi'");
// 如果 $guishudi = " "(一个空格),就存进去了
修复方案
1. 立即修复数据(清理数据库)
sql
-- 把所有只包含空格的值更新为空字符串
UPDATE pw_ipdatabase SET ipfrom = '' WHERE ipfrom = ' ';

-- 或者更彻底:去除首尾空格
UPDATE pw_ipdatabase SET ipfrom = TRIM(ipfrom) WHERE ipfrom LIKE '% %';
2. 修复代码逻辑(最重要!)
所有判断都要用 trim() 处理:

php
// 方法1:在获取值时自动去除空格
$guishu = trim($rs['ipfrom'] ?? '');

// 方法2:在所有判断中使用 trim()
if ($rs && trim($rs['ipfrom']) !== '' && trim($rs['ipfrom']) !== null) {
    return trim($rs['ipfrom']);
}
3. 完整修复代码
php
// 1. 查询数据库
$newip = preg_replace('/\.\d+$/', '', $onlineip);
$kw = $newip . '%';
$rs = $db->get_one("SELECT * FROM pw_ipdatabase WHERE onlineip LIKE '$kw' ORDER BY id DESC LIMIT 1");

if ($rs) {
    // 关键:去除首尾空格后再判断
    $ipfrom = trim($rs['ipfrom'] ?? '');
    
    // 如果有有效归属地(非空)
    if ($ipfrom !== '') {
        return $ipfrom;
    }
    
    // 如果归属地为空(包括空格、空字符串、NULL),尝试更新
    $guishudi = getLocationFromPconline($onlineip);
    if (!empty($guishudi)) {
        $guishudi = trim($guishudi);  // 去除可能的空格
        $db->query("UPDATE pw_ipdatabase SET ipfrom='$guishudi', source='pconline.com.cn' WHERE id={$rs['id']}");
        return $guishudi;
    }
    // 获取失败,更新为空字符串
    $db->query("UPDATE pw_ipdatabase SET ipfrom='' WHERE id={$rs['id']}");
    return '';
}

// 2. 没有记录,获取归属地
$guishudi = '';
// 尝试 ip.cn
// ...

// 尝试 pconline
if (empty($guishudi)) {
    $guishudi = getLocationFromPconline($onlineip);
}

// 3. 插入数据库(去除空格)
$guishudi = trim($guishudi);
if (!empty($guishudi)) {
    $db->query("INSERT INTO pw_ipdatabase SET onlineip='$onlineip', ipfrom='$guishudi', source='$source'");
} else {
    $db->query("INSERT INTO pw_ipdatabase SET onlineip='$onlineip', ipfrom=''");
}

return $guishudi;
4. 在所有解析归属地的地方都加上 trim()
php
// 解析 ip.cn 时
if (isset($str3[1])) {
    $str4 = strip_tags($str3[1]);
    $str4 = str_replace(' ', '', $str4);
    $str4 = trim($str4);  // ← 一定要加这个!
    if (!empty($str4)) {
        $zhuanhuan = utf8ToNumericEntities($str4);
        $shanchu = explode('<', $zhuanhuan);
        $guishudi = trim($shanchu[0] ?? '');  // ← 加 trim
    }
}

// 解析 pconline 时
function getLocationFromPconline($ip) {
    // ...
    if (preg_match('/"addr":"([^"]+)"/', $response, $matches)) {
        return trim($matches[1]);  // ← 加 trim
    }
    return '';
}
教训总结
永远用 trim() 处理用户输入和外部数据(网页解析、API返回等)

数据库查询后也要用 trim() 处理,防止隐藏字符

empty(" ") 返回 false,这是一个常见陷阱!

数据验证要全面:不能用 empty() 判断所有"空"值,因为空格不算空

你的问题完全是一个空格引发的血案!😄

现在你知道该怎么修复了吗?需要我帮你把完整的函数重写一遍吗?
尊重每位祺师,做有价值的祺文化社区!欢迎光临,并感谢您的支持与陪伴!
我希望能通过这个论坛让更多的朋友更多的关爱自己的家人和朋友,不要等到有一天失去后才知道没有好好的珍惜,才知道
拥有的珍贵!亲爱的们,如果你懂得,请把这份关爱传递,让更多的人加入到爱心接力当中,让每个人的心中都洋溢着爱的温馨!

我们应该知道:岁月如梭!其实生命,是很短暂,也是很脆弱的东西…… 设置自己的帖子签名请点这里!