这篇文章总结了10个新手最容易犯的错误,建议收藏,下次遇到直接对照排查。
错误1:用字符串拼接SQL,把自己网站变成"公共厕所"
//错误写法$id = $_GET['id'];$sql = "SELECT * FROM users WHERE id = " . $id;$result = mysqli_query($conn, $sql);
为什么错: 这是SQL注入的经典入口。攻击者只需要在URL后面加个 ?id=1 OR 1=1,你的数据就全暴露了。// 正确写法$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");$stmt->execute(['id' => $_GET['id']]);$user = $stmt->fetch();
一句话总结: 永远不要用字符串拼接SQL,PDO预处理语句是底线。
错误2:线上环境开错误提示,把服务器配置暴露给全世界
// php.ini 里的危险配置display_errors = On
为什么错: 线上报错会直接显示文件路径、数据库结构、甚至SQL语句。攻击者看了都感动哭了,连扫描工具都省了。// 正确配置; 线上环境display_errors = Offlog_errors = Onerror_log = /var/log/php_errors.log
错误3:在函数里直接访问外部变量,然后怀疑人生
// 错误写法$name = "张三";functionsayHello() { echo "你好," . $name; // 报错:Undefined variable $name}sayHello();
为什么错: PHP的函数作用域是独立的,外部变量不会自动进入函数内部。// 正确写法functionsayHello(string$name): void{ echo "你好," . $name;}sayHello($name);// 或者用匿名函数的use$greet = function() use ($name) { echo "你好," . $name;};
一句话总结: PHP函数是"孤岛",变量传进去要么靠参数,要么靠 use。
错误4:用 @ 符号抑制错误,相当于把汽车故障灯用胶带贴上
// 错误写法$content = @file_get_contents('https://example.com/api');
为什么错:@ 只是让错误不显示,问题依然存在。API挂了、文件不存在、网络超时,你全都不知道,后面代码继续跑,产生连锁反应。
// 正确写法$content = file_get_contents('https://example.com/api');if($content === false) { // 记录日志,返回友好提示 error_log('API请求失败'); return ['error' => '服务暂时不可用'];}
一句话总结:@ 是PHP最危险的运算符,能不用就不用。
错误5:不处理用户输入,让XSS攻击在你网站上开派对
// 错误写法echo "<div>用户留言:" . $_POST['comment'] . "</div>";
为什么错: 如果用户输入 <script>location.href='https://钓鱼网站.com'</script>,所有看这条留言的人都会被跳转。
// 正确写法echo "<div>用户留言:" . htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8') . "</div>";
一句话总结: 所有输出到页面的用户输入,都要过一遍 htmlspecialchars()。
错误6:数组键名的"变形记",字符串数字键神秘消失
// 让你困惑的代码$arr = [ "1" => "字符串1", 1 => "整数1", "01" => "字符串01"];print_r($arr);// 结果:// Array// (// [1] => 整数1 ← "1" 被覆盖了!// [01] => 字符串01 ← 这个保留,因为"01" != 1// )
为什么错: PHP会自动把纯数字字符串(如 "1"、"42")转换成整数作为数组键。"1" 和 1 被认为是同一个键,后者覆盖前者。
// 正确写法:如果需要保留字符串键,加前缀$arr = [ "id_1" => "字符串1", "id_01" => "字符串01"];
一句话总结: PHP数组键遇到纯数字字符串会自动变整数,需要字符串键就加前缀。
错误7:== 和 === 的坑,strpos() 返回0被误判为"没找到"
这就是那个曾经让我查了3小时的bug。
事情是这样的。那天我在写一个敏感词过滤功能:
// 错误写法(伪代码)$content = "欢迎来到我的网站";$badWord = "欢迎";$pos = strpos($content, $badWord);if($pos == false) { echo "内容安全,可以发布";} else { echo "包含敏感词,位置:" . $pos;}
你猜输出什么?
我当场就懵了。"欢迎" 明明在开头啊!strpos 怎么会返回false?
真相是:
我查了3小时,从编码问题怀疑到PHP源码,最后才发现是 == 和 === 的区别。
// 正确写法$pos = strpos($content, $badWord);if($pos === false) { // 注意三个等号! echo "内容安全,可以发布";} else { echo "包含敏感词,位置:" . $pos;}
== 和 === 的核心区别:
// 另一个经典场景:接口返回值判断$response = file_get_contents('https://api.example.com/data'); // 返回 "0"if ($response == false) { // 会进这里!因为 "0" == false 是 true echo "请求失败";}if ($response === false) { // 只有真正的失败(返回false)才会进这里 echo "请求失败";} else { echo "返回内容是:" . $response; // 输出 0}
一句话总结:PHP里判断返回值,尤其是 strpos()、file_get_contents()、stripos() 这些,永远用 ===,否则0和false会让你怀疑人生。
错误8:用 == 比较浮点数,金额计算差一分钱
// 错误写法$a = 0.1;$b = 0.7;$sum = $a + $b;if($sum == 0.8) { echo "相等";} else { echo "不相等"; // 会输出这个!}
为什么错: 计算机用二进制存储浮点数,0.1 + 0.7 实际是 0.7999999999999999。
// 正确写法if(abs($sum - 0.8) < 0.00001) { echo "相等";}// 金额计算用整数(分)$price = 1000; // 10.00元$discount = 150; // 1.50元$final = ($price - $discount) / 100; // 8.50元
一句话总结: 金额别用浮点数,用整数存"分";必须比较浮点数时,用 abs($a - $b) < 0.00001。
错误9:在循环里查数据库,服务器被拖垮的N+1问题
// 错误写法$users = $db->query("SELECT * FROM users LIMIT 100");foreach($users as $user) { // 每次循环都查一次数据库!101次查询 $orders = $db->query("SELECT * FROM orders WHERE user_id = " . $user['id']); $user['orders'] = $orders;}
为什么错: 100个用户就是101次查询。用户量一上来,数据库直接挂掉。
// 正确写法:用IN查询一次取出$userIds = array_column($users, 'id');$placeholders = implode(',', array_fill(0, count($userIds), '?'));$stmt = $pdo->prepare("SELECT * FROM orders WHERE user_id IN ($placeholders)");$stmt->execute($userIds);$allOrders = $stmt->fetchAll();// 用PHP分组$ordersMap = [];foreach($allOrders as $order) { $ordersMap[$order['user_id']][] = $order;}foreach($users as &$user) { $user['orders'] = $ordersMap[$user['id']] ?? [];}
一句话总结: 循环里别查数据库,能用一次查询解决的,绝不用两次。
错误10:还在用 mysql_* 函数
// 错误写法(已废弃,PHP7+直接报错)$conn = mysql_connect('localhost', 'root', '123456');mysql_select_db('test', $conn);$result = mysql_query('SELECT * FROM users');
为什么错:mysql_* 系列函数在PHP 5.5被废弃,PHP 7.0直接移除。还在用的代码一升级PHP版本就全崩。
// 正确写法// PDO(推荐)$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '123456');// 或者 mysqli$conn = new mysqli('localhost', 'root', '123456', 'test');
写在最后
虽然现在都是直接各种框架但是基本的常识还是必须要明白掌握的
编程这件事,踩坑不可怕,可怕的是同一个坑踩两次。