php ?? 运算符导致的 bug 问题解析
?? 不是三元运算符的缩写,问了 deepseek 才知道叫 NULL合并运算符。之前也见过,但都没当回儿事,应该感觉跟 ?: 三元运算符差不多。这次碰着 bug 了,想要严肃的分析记录一下。
$equipment->atime = ($remote['atime'] && $remote['atime'] != '0000-00-00 00:00:00') ?? ($equipment->atime ?? $equipmentFields['atime']['default']);
# 一开始的写法
$equipment->atime = $remote['atime'] ?? ($equipment->atime ?? $equipmentFields['atime']['default']);这里需要对设备日期更新做一个判断,如果传递过来的值不存在或者为空,就取当前设备的日期数据,最后不行就取默认值。
结果就是 orm 更新失败,通过 error_log 定位确认是 $equipment->save() 导致的。打开链接的 mysql 日志,重复调用找到执行的 sql,放到 navicat 工具里执行,确认是因为 atime 数据库字段为类型为 int(11),而传值为空字符串,类型不一致导致的失败。
将新增加的 && $remote['atime'] != '0000-00-00 00:00:00' 判断条件去掉,就没有报错。通过日志确认 $equipment->atime 为 1,而前面的 $remote['atime'] 未定义(被 unset 掉了)。
简化一下例子就是
> $arr = ['a'=>1, 'b'=>2];
> echo ($arr['c'] && $arr['c'] !='123') ?? 1;
PHP Notice: Undefined index: c in php shell code on line 1
Notice: Undefined index: c in php shell code on line 1
> echo $arr['c'] ?? 1;
1
> echo @($arr['c'] && $arr['c'] !='123') ?? 1;测试结果是这样的,但为什么未定义返回的是空呢,不应该是 1 吗?
为什么不是预期的 1
关键点在于:1.$arr['c'] 返回 NULL(并触发警告)
2.NULL 在布尔上下文中被视为 false
3.false && ... 直接短路返回 false(不是 NULL)
4.?? 运算符只检查 NULL,false 不是 NULL,所以不触发默认值
而 false 在转换为字符串时,会成为空字符串。这就是 sql 里显示为 '' 的原因。
暂时还不清楚为什么 false 在 orm 更新赋值的过程中,变成了空字符串。
所以这里要么去掉 && 运算部分,要么改成三元运算符:
$equipment->atime = ($remote['atime'] && $remote['atime'] != '0000-00-00 00:00:00') ? $remote['atime'] :($equipment->atime ?? $equipmentFields['atime']['default']); 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
海滨擎蟹
微信
支付宝