PHP 連接 MySQL 歷經三代 API:mysql_* (已移除)、mysqli (增強)、PDO (抽象層)。本文速查核心函數、預處理、事務、錯誤處理對比。
| 擴展 | PHP 版本 | 狀態 | 推薦度 |
|---|---|---|---|
| mysql | ≤ 5.6 | 已移除 (7.0+) | ❌ 禁用 |
| mysqli | 5.0+ | 維護中 | ✅ 僅 MySQL 專用項目 |
| PDO | 5.1+ | 維護中 | ✅✅ 首選 (跨庫、預處理、現代) |
set_charset('utf8mb4');
// PDO (推薦)
$pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
query('SELECT * FROM users');
while ($row = $res->fetch_assoc()) { ... }
$res->free();
// PDO
$stmt = $pdo->query('SELECT * FROM users');
while ($row = $stmt->fetch()) { ... }
// 無需顯式釋放,變量銷毀自動釋放
query("INSERT INTO users (name) VALUES ('test')");
$id = $mysqli->insert_id;
$affected = $mysqli->affected_rows;
// PDO
$pdo->exec("INSERT INTO users (name) VALUES ('test')");
$id = $pdo->lastInsertId();
$affected = $stmt->rowCount(); // 需用 exec 或 prepare+execute
prepare('SELECT * FROM users WHERE id = ? AND status = ?');
$stmt->bind_param('ii', $id, $status); // 類型: i/d/s/b
$stmt->execute();
$res = $stmt->get_result();
while ($row = $res->fetch_assoc()) { ... }
$stmt->close();
// PDO
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id AND status = :status');
$stmt->execute([':id' => $id, ':status' => $status]);
while ($row = $stmt->fetch()) { ... }
begin_transaction();
try {
$mysqli->query("INSERT ...");
$mysqli->query("UPDATE ...");
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
throw $e;
}
// PDO
$pdo->beginTransaction();
try {
$pdo->exec("INSERT ...");
$pdo->exec("UPDATE ...");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
query("...") or throw new Exception($mysqli->error);
// PDO (異常模式 - 推薦)
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$pdo->query("...");
} catch (PDOException $e) {
error_log($e->getMessage());
// 用戶友好提示
}
| 功能 | mysql | mysqli | PDO |
|---|---|---|---|
| 獲取關聯陣列 | mysql_fetch_assoc | $res->fetch_assoc() | $stmt->fetch(PDO::FETCH_ASSOC) |
| 獲取數字索引陣列 | mysql_fetch_row | $res->fetch_row() | $stmt->fetch(PDO::FETCH_NUM) |
| 獲取對象 | mysql_fetch_object | $res->fetch_object() | $stmt->fetch(PDO::FETCH_OBJ) |
| 獲取所有 | 無 (需循環) | $res->fetch_all(MYSQLI_ASSOC) | $stmt->fetchAll() |
| 字段數 | mysql_num_fields | $res->field_count | $stmt->columnCount() |
| 行數 | mysql_num_rows | $res->num_rows | $stmt->rowCount() (僅緩衝查詢準確) |
| 移動指針 | mysql_data_seek | $res->data_seek() | 不支援 (改用 fetchAll) |
| 獲取單值 | mysql_result | $res->fetch_column() | $stmt->fetchColumn() |
函數名對應規律:mysqli_function($link, ...) ↔ $mysqli->function(...)。建議統一使用面向對象風格,代碼更清晰、IDE 提示更好。
$stmt->debugDumpParams() 查看綁定參數PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false (需 mysqlnd)PDO::MYSQL_ATTR_MULTI_STATEMENTS => true (慎用)$stmt->getColumnMeta($i)mysql_* → mysqli:語法相近,機械替換 + 加預處理PDO:統一介面、支援 SQLite/PostgreSQL/Oracle 遷移db->query()/db->execute()mysql_* 歷史使命結束,mysqli 適合純 MySQL 高性能場景,PDO 以統一介面、真實預處理、跨數據庫、異常處理成為現代 PHP 專案首選。核心原則:永遠使用預處理語句,永不拼接 SQL。