PHP 連接 MySQL 主流有兩種擴充:mysqli(MySQL Improved,僅支援 MySQL)與 PDO(PHP Data Objects,支援多種資料庫)。本文比較兩者語法、預處理、錯誤處理差異,並詳細解決跨平台中文亂碼問題。
; php.ini extension=mysqli extension=pdo_mysql ; 確認 extension_dir 指向正確的 ext 目錄 ; Windows: extension_dir = "ext" ; Linux: extension_dir = "/usr/lib/php/extensions/no-debug-non-zts-20220829" ; 重啟 Web 伺服器 / PHP-FPM 後驗證 php -m | grep -E 'mysqli|pdo_mysql'
<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'password', 'dbname', 3306);
// 連線錯誤處理
if ($mysqli->connect_errno) {
die("連線失敗: {$mysqli->connect_error}");
}
// 關鍵:設定字元集,防止中文亂碼
$mysqli->set_charset('utf8mb4'); // 推薦 utf8mb4 支援 Emoji
// 查詢
$sql = "SELECT id, name, email FROM users ORDER BY id DESC";
$result = $mysqli->query($sql);
if ($result) {
while ($row = $result->fetch_assoc()) {
echo "ID: {$row['id']}, Name: {$row['name']}, Email: {$row['email']}<br>";
}
$result->free(); // 釋放結果集
}
// 預處理防止 SQL 注入
$stmt = $mysqli->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param('ss', $name, $email);
$name = '張三'; $email = 'zhang@example.com';
$stmt->execute();
echo "新增 ID: {$stmt->insert_id}";
$mysqli->close();
?>
<?php
$dsn = 'mysql:host=127.0.0.1;dbname=dbname;charset=utf8mb4';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 拋出例外
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 關聯陣列
PDO::ATTR_EMULATE_PREPARES => false, // 使用真實預處理
];
try {
$pdo = new PDO($dsn, 'user', 'password', $options);
// 查詢
$stmt = $pdo->query("SELECT id, name FROM users LIMIT 10");
foreach ($stmt as $row) {
echo "{$row['id']}: {$row['name']}<br>";
}
// 預處理
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute(['李四', 'li@example.com']);
echo "新增 ID: {$pdo->lastInsertId()}";
// 交易
$pdo->beginTransaction();
$pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
$pdo->commit();
} catch (PDOException $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
die("資料庫錯誤: {$e->getMessage()}");
}
?>
| 特性 | mysqli | PDO |
|---|---|---|
| 資料庫支援 | 僅 MySQL/MariaDB | 12+ 驅動 |
| API 風格 | 面向物件 + 程序式 | 純面向物件 |
| 預處理 | 支援 (bind_param) | 支援 (bindValue/execute) |
| 命名參數 | 不支援 | 支援 (:name) |
| 交易控制 | begin_transaction() | beginTransaction() |
| 錯誤模式 | 手動檢查或 mysqli_report | 例外模式 (預設可配置) |
| 效能 | 略快 (原生) | 極微差距,可忽略 |
CHARSET=utf8mb4SET NAMES utf8mb4 / mysqli::set_charset / PDO DSN charset<meta charset="utf-8"> / header('Content-Type: text/html; charset=utf-8')任一環節不匹配即產生亂碼。
# 1. 資料庫建表
CREATE TABLE t (c VARCHAR(100)) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
# 2. PHP 連線 (mysqli)
$mysqli->set_charset('utf8mb4');
# 3. PHP 連線 (PDO)
$dsn = 'mysql:host=...;dbname=...;charset=utf8mb4';
# 4. PHP 檔案編碼
; 編輯器設定 "UTF-8 without BOM"
# 5. HTTP 輸出
header('Content-Type: text/html; charset=utf-8');
; 或 nginx/apache 設定
add_header Content-Type "text/html; charset=utf-8";
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;SET NAMES gbk 讀取舊資料,寫入前 iconv('GBK','UTF-8',$str) 轉碼,漸進式遷移。Class 'mysqli' not found → 未啟用 extension=mysqli 或 extension_dir 錯誤。Authentication plugin 'caching_sha2_password' not supported → PHP 版本過舊或 MySQL 8.0 預設插件不支援,建議建立用戶指定 mysql_native_password 或升級 PHP。MySQL server has gone away → 連線逾時,需實作重連邏輯或調大 wait_timeout。新專案首選 PDO:支援多資料庫、命名參數、例外錯誤模式、真實預處理。mysqli 適合僅針對 MySQL 的高效能舊專案維護。亂碼核心在於「連線字元集」與「資料庫字元集」一致,統一使用 utf8mb4 並從資料庫、連線、PHP、HTML 四層落實,即可徹底杜絕中文亂碼。