PHP 編譯器在遇到第一個語法錯誤時即停止解析,報錯行號往往不是真正錯誤位置。能快速定位「表面現象」背後的「根本原因」,是資深開發者與新手的核心差異之一。本文系統梳理常見錯誤類型、誤導機制與調試技巧。
Parse error: syntax error, unexpected T_IF in /path/script.php on line 4
第 4 行 if (...) 語法完全正確,卻報 unexpected T_IF。
PHP 解析器按 Token 流處理。當第 3 行 $b = "value" 缺少分號結束時,解析器認為語句未結束,繼續向後吞噬 Token,直到遇到 if (保留字 T_IF) 才發現「這裡不應該出現語句開頭關鍵字」,拋出錯誤並指向 if 所在行。
| 代碼缺陷 | 報錯 Token | 實際修復位置 |
|---|---|---|
語句缺少分號 ; | T_IF / T_WHILE / T_FOR / T_FOREACH / T_FUNCTION | 上一行結尾 |
字符串未閉合引號 ' " | T_STRING / T_VARIABLE / T_ECHO | 字符串起始行 |
大括號 { } 不匹配(少閉合) | T_VARIABLE / $ (EOF) | 函數/類/控制結構起始處 |
保留字作變量名 $while = 1; | T_WHILE | 賦值語句處 |
Warning: mysqli_fetch_array() expects parameter 1 to be mysqli_result, bool given in ...
報錯行:while($row = mysqli_fetch_array($result))
$result 是 false,說明 mysqli_query() 執行失敗。原因通常是:
$sql = "SELECT * FROM `users` WHERE `email` = ?";
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
error_log("Prepare failed: " . $mysqli->error);
die("系統錯誤");
}
$stmt->bind_param("s", $email);
if (!$stmt->execute()) {
error_log("Execute failed: " . $stmt->error);
die("查詢失敗");
}
$result = $stmt->get_result();
// 此處 $result 保證是 mysqli_result 或 false(無結果集)
| 錯誤類型 | 典型代碼 | 風險 | 修復 |
|---|---|---|---|
| Undefined variable | echo $name; (未初始化) | 邏輯漏洞、安全隱患 | $name = $_GET['name'] ?? ''; |
| Undefined array key | $id = $_POST['id']; | 注入風險、流程中斷 | $id = $_POST['id'] ?? 0; |
| Trying to access array offset on null | $user['name'] ($user 為 null) | PHP 8.0+ 拋 Error | $user['name'] ?? 'Guest' |
| Division by zero | $avg = $sum / $count; | PHP 8.0+ 拋 DivisionByZeroError | $count ?: 1 |
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php_errors.log');
dump($var) 替代 var_dump,支持 Symfony/Laravel 原生phpstan analyse src --level=5 捕獲 80%+ 潛在類型錯誤display_errors = Off,log_errors = Onset_exception_handler() 返回友好 JSON/HTMLtry-catch,異步任務加入重試與死信隊列核心心法:錯誤信息是編譯器給開發者的「線索」,而非「判決」。養成「先信代碼邏輯,再信報錯行號」的逆向思維,調試效率將質變。