PDO (PHP Data Objects) 提供統一的數據庫訪問接口,支援 12 種數據庫驅動,僅支援面向對象 API,是現代 PHP 開發推薦的數據庫訪問方式。
<?php
try {
$test = new PDO(); // 僅測試類是否存在
echo "PDO 已安裝";
} catch (Error $e) {
die("PDO 未安裝,請在 php.ini 啟用 extension=pdo_mysql 等驅動");
}
?>
<?php
$dsn = 'mysql:dbname=demo;host=localhost;port=3306;charset=utf8mb4';
$username = 'root';
$password = 'password';
try {
$db = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 拋出異常
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默認關聯數組
PDO::ATTR_EMULATE_PREPARES => false, // 使用真實預處理
]);
} catch (PDOException $e) {
die('數據庫連接失敗: ' . $e->getMessage());
}
?>
DSN 格式:`驅動:參數1=值1;參數2=值2`。MySQL 必須指定 `charset=utf8mb4` 避免亂碼。
<?php
$stmt = $db->query("SELECT * FROM foods WHERE healthy = 0");
$foods = $stmt->fetchAll(); // 獲取所有行
foreach ($foods as $food) {
echo $food['name'] . "
";
}
echo "共 {$stmt->rowCount()} 筆記錄"; // MySQL 支援 rowCount()
?>
<?php
$deleted = $db->exec("DELETE FROM foods WHERE healthy = 1");
echo "刪除 {$deleted} 行";
?>
<?php $input = "this is's' a '''pretty dange'rous str'ing"; $safe = $db->quote($input); // 結果: 'this is\'s\' a \'\'\'pretty dange\'rous str\'ing' ?>
<?php
// 匿名佔位符 ?
$stmt = $db->prepare('SELECT * FROM foods WHERE name = ? AND healthy = ?');
$stmt->execute(['Cake', true]);
$cake = $stmt->fetch();
// 命名佔位符 :name
$stmt2 = $db->prepare('SELECT * FROM foods WHERE name = :name AND healthy = :healthy');
$stmt2->execute([':name' => 'Pie', ':healthy' => false]);
$pie = $stmt2->fetch();
// 批量綁定
$stmt->bindValue(1, 'Cake');
$stmt->bindValue(2, true);
$stmt->execute();
// bindParam 綁定引用 (僅執行時取值)
$name = 'Pie';
$stmt2->bindParam(':name', $name);
$stmt2->execute();
?>
關鍵配置:`PDO::ATTR_EMULATE_PREPARES => false` 強制使用真實預處理,防止某些驅動模擬預處理導致的注入風險。
<?php
try {
$db->beginTransaction();
$db->exec("INSERT INTO orders (user_id, amount) VALUES (1, 100)");
$db->exec("UPDATE accounts SET balance = balance - 100 WHERE user_id = 1");
$db->exec("INSERT INTO logs (action) VALUES ('order_created')");
$db->commit();
echo "事務提交成功";
} catch (Exception $e) {
$db->rollBack();
echo "事務回滾: " . $e->getMessage();
}
?>
注意:`rollBack()` 無法回滾 DDL 語句(如 `DROP TABLE`),MySQL 需使用 InnoDB 引擎支援事務。
| 屬性常量 | 說明 | 常用值 |
|---|---|---|
| ATTR_DEFAULT_FETCH_MODE | 默認獲取模式 | FETCH_ASSOC, FETCH_OBJ, FETCH_CLASS |
| ATTR_ERRMODE | 錯誤處理模式 | ERRMODE_EXCEPTION (推薦), ERRMODE_WARNING, ERRMODE_SILENT |
| ATTR_TIMEOUT | 查詢超時秒數 | 整數,超時拋出異常 |
| ATTR_EMULATE_PREPARES | 是否模擬預處理 | false (推薦使用真實預處理) |
| ATTR_STRINGIFY_FETCHES | 數值/布爾轉字符串 | true/false |
| ATTR_PERSISTENT | 持久連接 | true/false (慎用,可能導致連接泄漏) |
<?php
class Food {
public $name;
public $healthy;
public function isHealthy() { return (bool)$this->healthy; }
}
$stmt = $db->query('SELECT * FROM foods');
$foods = $stmt->fetchAll(PDO::FETCH_CLASS, 'Food');
// $foods 為 Food 對象數組
foreach ($foods as $food) {
echo $food->name . ($food->isHealthy() ? ' ✓' : ' ✗') . "
";
}
?>
<?php
try {
$stmt = $db->prepare("INSERT INTO users (email) VALUES (?)");
$stmt->execute([$email]);
} catch (PDOException $e) {
if ($e->getCode() == 23000) { // 完整性約束違反
echo "郵箱已存在";
} else {
error_log("DB Error: " . $e->getMessage());
echo "系統繁忙,請稍後重試";
}
}
?>
PDO 以統一接口、真實預處理支援、靈活獲取模式、異常處理機制,成為 PHP 數據庫訪問的首選。建議:
1. 始終啟用 `ERRMODE_EXCEPTION` 和 `ATTR_EMULATE_PREPARES => false`
2. 優先使用預處理語句
3. 合理使用事務保證數據一致性
4. 根據業務選擇 `FETCH_ASSOC` 或 `FETCH_CLASS`
5. 避免長連接 (`ATTR_PERSISTENT`),改用連接池中間件