核心原則:失敗快速、訊息清楚、不洩露細節
生產環境勿直接輸出資料庫錯誤給使用者,改記錄日誌、顯示友善訊息。
標準連線與查詢流程 (PDO)
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT id, name, email FROM users WHERE status = ?');
$stmt->execute(['active']);
$users = $stmt->fetchAll();
foreach ($users as $u) {
echo htmlspecialchars($u['name']) . ' - ' . htmlspecialchars($u['email']) . '
';
}
} catch (PDOException $e) {
// 記錄詳細錯誤 (含 SQL、參數、堆疊)
error_log('DB Error: ' . $e->getMessage() . ' | File: ' . $e->getFile() . ':' . $e->getLine());
// 使用者看到友善訊息
http_response_code(500);
echo '系統暫時無法處理您的請求,請稍後再試。';
}
分層錯誤處理策略
| 層級 | 動作 | 範例 |
| 連線失敗 | 記錄、顯示維護頁、告警 | 資料庫掛掉、憑證錯誤、連線數耗盡 |
| 查詢語法錯誤 | 記錄、回傳 500、開發環境顯示詳情 | SQL 拼寫錯誤、欄位不存在 |
| 資料完整性違反 | 捕獲特定 SQLSTATE、回傳 400/409、提示使用者 | 唯一鍵衝突 (23000)、外鍵違反 (23000) |
| 資料不存在 | 回傳 404 或空集合 | ID 查無資料 |
| 死結/鎖等待 | 指數退避重試 3 次、仍失敗則記錄告警 | SQLSTATE HY000 / 40001 |
常見錯誤代碼對照
1062 (23000) : Duplicate entry - 唯一鍵衝突
1452 (23000) : Cannot add/update child row - 外鍵違反
1213 (40001) : Deadlock found - 死結
1205 (HY000) : Lock wait timeout exceeded - 鎖等待逾時
2006 (HY000) : MySQL server has gone away - 連線中斷
2013 (HY000) : Lost connection during query - 查詢中斷連線
除錯輔助函數
SQL: $sql\nParams: " . json_encode($params) . "
";
}
}
function executeWithRetry(PDO $pdo, string $sql, array $params = [], int $maxRetries = 3): PDOStatement {
$delay = 100; // ms
for ($i = 0; $i < $maxRetries; $i++) {
try {
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
if ($e->getCode() === '40001' || $e->getCode() === 'HY000') { // deadlock/lock wait
usleep($delay * 1000);
$delay *= 2;
continue;
}
throw $e;
}
}
throw new RuntimeException("Max retries exceeded for: $sql");
}
開發環境除錯工具
- PHPStorm / VS Code + Xdebug:斷點、變數檢查、SQL 分析
- Laravel Telescope / Symfony Profiler:請求、查詢、記憶體、時間軸
- MySQL 慢查詢日誌:
long_query_time=1、log_queries_not_using_indexes
EXPLAIN ANALYZE (MySQL 8.0.18+) 分析執行計畫
安全性檢查清單
- [ ] 所有查詢使用預備語句
- [ ] 錯誤訊息不含 SQL、參數、堆疊輸出給前端
- [ ] 資料庫帳號最小權限、非 root
- [ ] 敏感欄位加密儲存 (密碼用 password_hash)
- [ ] 連線字串不寫在程式碼 (用環境變數/.env)
- [ ] 定期備份、測試還原
- 上一篇文章: PHP + MySQL 分頁機制效能比較:LIMIT vs 游標位移
- 下一篇文章: PHP + MySQL 動態網站開發 10 大經驗與效能優化技巧
Copyright ©
程式碼之家 All Rights Reserved