PHP里那些看起来对,但会坑死人的写法
你有没有过这种经历?代码写完,本地跑没问题,提测没问题,上线也没问题 ——结果某天半夜,线上突然炸了。翻日志,定位到一行代码,你盯着它看了三遍,逻辑完全没问题啊。但就是这行看起来正确的代码,坑了你。今天聊几个 PHP 最常见的隐形坑,每一个,都是生产环境真金白银买来的教训。01 浮点数比较:0.1+0.2 永远不等于 0.3
这个坑不怪 PHP,几乎所有语言都有。
但 PHP 自动类型转换多,特别容易踩。$a = 0.1 + 0.2;if ($a == 0.3) { echo "相等";} else { echo "不相等";}输出:不相等因为 0.1+0.2 实际是:0.30000000000000004✅ 正确写法if (bccomp($a, '0.3', 2) === 0) { echo "相等";}涉及金额、计算精度,永远不要直接用 == 比较浮点数。
02foreach 里 unset,数组索引断裂
你以为删干净了,其实数组索引直接裂开。$data = ['a', 'b', 'c', 'd', 'e'];foreach ($data as $key => $value) { if ($value === 'c') { unset($data[$key]); }}结果数组变成:0:a, 1:b, 3:d, 4:e再用 for 循环遍历:for ($i=0; $i<count($data); $i++) { echo $data[$i];}直接报错:Undefined offset: 2因为unset之后数组的key不会自动重新索引,$data已经不存在了,但循环计数器$i依然会走到2。正确的姿势:
unset之后记得加一行array_values来重建索引:$data = array_values($data);或者,更优雅的做法是直接使用array_filter来过滤数组,避免在遍历中修改数组结构。小细节,但在for循环里能坑你一整天。
03strtotime的2038问题:时间突然变成1970年
这个坑藏得深,虽然现在服务器大多是64位,但在Windows环境或老旧系统中,它依然是一颗定时炸弹。echo date('Y-m-d', strtotime('2040-01-01'));在32位系统(或Windows下的PHP)中,输出可能是:1970-01-01不是2040年,直接给你回老家了。原因:
strtotime返回的是整数时间戳。在32位系统中,整数最大值是2147483647,对应2038年1月19日。超过这个时间,整数溢出变成负数,date解析出来就是1970年。怎么防?用DateTime类:$date = new DateTime('2040-01-01');echo $date->format('Y-m-d');DateTime类不受32位整数限制,且代码可读性更强。别再满屏的strtotime和date了,拥抱面向对象的时间处理吧。
04empty的误杀:0也是空?
这个坑新人踩得最多。$score = 0;if (empty($score)) { echo "没有分数";}输出:没有分数。但0是一个合法的分数啊!它不是“没有”。empty会把以下值都判为true:""(空字符串)0(整数零)0.0(浮点零)"0"(字符串零)nullfalse[](空数组)所以如果你的业务里0是合法值,千万别用empty,用isset或者显式判断:if ($score === null) { echo "没有分数";}一个等号之差,线上少一个Bug。
05json_encode中文变unicode
$arr = ['name' => '张三'];echo json_encode($arr);输出:{"name":"\u5f20\u4e09"}中文全变成unicode转义了。前端拿到还要再转一次,多此一举。加个参数就好:echo json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);输出:{"name":"张三"}建议把这个参数组合刻在DNA里。JSON_UNESCAPED_UNICODE:防止中文变\u5f20。JSON_UNESCAPED_SLASHES:防止URL里的/变成\/。
06数组排序掉key
这个坑发生在你用sort的时候:$user = ['name' => '张三', 'age' => 25, 'city' => '上海'];sort($user);print_r($user);输出:Array ( => 上海 => 张三 => 25 )关联数组的key全没了,变成了数字索引。值还在,但键名全丢了,后面用$user['name']直接报Undefined index。关联数组排序用asort(按值排,保留key)或者ksort(按键排):asort($user); // key还在记住一个规则:sort系列会重建索引,asort系列保留键名。
写在最后
这几个坑,说实话,都不是什么高深的东西。但就是这样“不高级”的坑,在生产环境里炸起来最要命。因为你觉得它太简单了,不可能出错,所以从来不检查。分享给你的同事,让他们也少踩几个坑。你还在PHP里踩过什么离谱的坑?评论区说说,我整理出来下次发。 确实,浮点数比较这个坑太经典了,几乎每个PHP开发者迟早都会踩一次。除了用abs($a - 0.3) < 0.00001这种精度判断,更推荐直接用PHP的BCMath函数库进行高精度数学计算,比如bccomp('0.1'+'0.2', '0.3', 10),完全避免二进制浮点的误差问题。
另外,数组和字符串的隐式转换也特别容易出事,比如用字符串键名去取数组值,一不小心就拿到NULL或者意想不到的结果。建议关键地方多用isset和array_key_exists明确判断,别依赖自动转换。
这些坑平时不显眼,一到并发高或者数据量大的时候就突然爆发,真是防不胜防。大家平时写代码还是多严谨一点,尤其是涉及金额、计数等关键数据时,别嫌麻烦,该做的类型检查和精度处理一定要做。 太有共鸣了! 楼主总结的浮点数问题简直是血泪史,楼上兄弟提到的BCMath方案也很稳。我补充一个平时更容易忽略的坑:弱类型比较。
很多时候图省事用双等号,结果字符串和数字、甚至布尔值混在一起直接翻车。比如"0" == 0
和"0" == false
都是true,但业务逻辑根本不想这样。强烈建议养成用三等号的习惯,或者直接开启declare(strict_types=1);
,配合静态检查工具跑一遍,能提前揪出绝大部分隐患。
另外数组遍历的时候如果用了引用赋值,循环外面要是没注意,变量值就被偷偷改了,排查起来真的头大。大家写核心逻辑还是多写点防御性代码吧,毕竟线上报警比改bug刺激多了。欢迎继续补充踩坑经历!
頁:
[1]