文章原始代碼使用 mysql_connect、mysql_query 等函數,自 PHP 5.5 棄用、PHP 7.0 完全移除。現代項目**必須使用 PDO 或 mysqli**。本文保留原邏輯作對比,重點講解 PDO 規範寫法與亂碼根治方案。
<?php
// ❌ 已棄用寫法,僅供歷史參考
$link = mysql_connect("localhost", "root", "密碼");
if (!$link) echo "連接失敗!";
mysql_select_db("infosystem", $link);
mysql_query("SET NAMES GB2312"); // 編碼設置位置不當、易失效
$rs = mysql_query("SELECT * FROM info");
while ($row = mysql_fetch_array($rs)) {
echo "<tr><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>";
}
mysql_free_result($rs);
?>
痛點: 無預處理(SQL 注入風險)、編碼設置靠後且不穩定、錯誤處理原始、資源釋放易忘。
<?php
// config.php - 集中管理配置
return [
'dsn' => 'mysql:host=localhost;dbname=infosystem;charset=utf8mb4',
'username' => 'root',
'password' => '你的強密碼',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 拋出異常
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 返回關聯數組
PDO::ATTR_EMULATE_PREPARES => false, // 使用真實預處理
],
];
// db.php - 建立單例連接
class Database {
private static ?PDO $pdo = null;
public static function pdo(): PDO {
if (self::$pdo === null) {
$config = require __DIR__ . '/config.php';
self::$pdo = new PDO(
$config['dsn'],
$config['username'],
$config['password'],
$config['options']
);
}
return self::$pdo;
}
}
// 業務代碼
try {
$pdo = Database::pdo();
$stmt = $pdo->query("SELECT dept_name, emp_name, pc_name FROM info");
echo "<table><tr><th>部門</th><th>員工</th><th>PC</th></tr>";
while ($row = $stmt->fetch()) {
// 防 XSS:輸出前轉義
echo "<tr><td>" . htmlspecialchars($row['dept_name']) . "</td>";
echo "<td>" . htmlspecialchars($row['emp_name']) . "</td>";
echo "<td>" . htmlspecialchars($row['pc_name']) . "</td></tr>";
}
echo "</table>";
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
die("系統繁忙,請稍後再試");
}
亂碼本質是「編碼鏈路任一環節不一致」。完整鏈路:
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci(支援 Emoji)。charset=utf8mb4,或連接後執行 SET NAMES utf8mb4(PDO DSN 方式優先)。header('Content-Type: text/html; charset=utf-8'); 或 Nginx/Apache 配置。<meta charset="utf-8">。| 誤區 | 正確做法 |
|---|---|
用 SET NAMES utf8(無 mb4) | 必須 utf8mb4,否則 4 字節字符(Emoji、生僻字)截斷/亂碼 |
| 僅在 phpMyAdmin 設置字符集 | phpMyAdmin 只影響其自身會話,PHP 代碼需在 DSN 或連接後顯式設置 |
用 mysql_query("SET NAMES 'utf8'") 字符串拼接 | PDO 用 DSN 參數 charset=utf8mb4,mysqli 用 $mysqli->set_charset('utf8mb4') |
| GBK 項目強行混用 UTF-8 | 若歷史庫為 GBK,連接層用 charset=gbk,輸出頁面 charset=gbk,全鏈路統一即可 |
$dept = $_GET['dept'] ?? '';
$stmt = $pdo->prepare("SELECT emp_name, pc_name FROM info WHERE dept_name = ?");
$stmt->execute([$dept]);
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
echo htmlspecialchars($row['emp_name']) . ' - ' . htmlspecialchars($row['pc_name']) . '<br>';
}
核心優勢: SQL 與數據分離,徹底杜絕注入;同一條 SQL 重複執行時,數據庫只解析優化一次,性能更優。
mysql_connect → PDO 單例mysql_query → $pdo->query() / $pdo->prepare()->execute()mysql_fetch_array → $stmt->fetch(PDO::FETCH_ASSOC)mysql_real_escape_string → **刪除**,改用預處理mysql_free_result → **自動釋放**,無需手動utf8mb4 全鏈路try-catch + 記錄日志 + 友好提示告別 mysql_* 不僅是語法升級,更是安全與可維護性的重構。**PDO + utf8mb4 + 預處理 + 異常處理** 是現代 PHP 數據庫開發的「四件套」。從今天起,寫下的每一條 SQL 都應經得起代碼審查與安全掃描。