首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

PHP PDO 連接 MySQL 完全指南:從基礎 CRUD 到事務、預處理與異常處理

     編輯:MySQL 綜合教學

PHP PDO 連接 MySQL 完全指南:從基礎 CRUD 到事務、預處理與異常處理

PDO (PHP Data Objects) 自 PHP 5.1 起提供統一數據庫訪問層,支援 12 種驅動,採用預處理機制原生防禦 SQL 注入,是現代 PHP 開發的標準選擇。本文涵蓋安裝配置、連接模式、核心 API、事務控制、錯誤處理三大模塊。

一、環境準備與驅動啟用


; php.ini 啟用擴展 (去掉分號)
extension=pdo_mysql
; 如需其他數據庫:
; extension=pdo_pgsql
; extension=pdo_sqlite
; extension=pdo_oci

驗證:php -m | grep pdo 或 phpinfo() 檢查 PDO drivers => mysql。

二、建立連接 (DSN 字符串)


 true]);
    
    // 關鍵屬性設置
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 拋出異常
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); // 默認關聯數組
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // 使用原生預處理
    
    echo "Connected successfully";
} catch (PDOException $e) {
    die("Connection failed: " . $e->getMessage());
}
?>

三、核心增刪改查操作

1. 查詢 (返回結果集)


// 直接查詢 (無參數)
$stmt = $pdo->query('SELECT * FROM members');
$rows = $stmt->fetchAll(); // 獲取所有行
foreach ($rows as $row) { /* ... */ }

// 單行獲取
$row = $stmt->fetch(PDO::FETCH_ASSOC);

// 逐行迭代 (內存友好)
while ($row = $stmt->fetch()) { /* ... */ }

2. 預處理語句 (參數化查詢,防注入核心)


// 命名佔位符 (推薦,可讀性強)
$sql = 'INSERT INTO `user` (`login`, `password`) VALUES (:login, :password)';
$stmt = $pdo->prepare($sql);
$stmt->execute([':login' => 'kevin', ':password' => password_hash('123', PASSWORD_BCRYPT)]);
$lastId = $pdo->lastInsertId();

// 問號佔位符 (位置綁定)
$stmt = $pdo->prepare('UPDATE `user` SET `password` = ? WHERE `user_id` = ?');
$stmt->execute([password_hash('456', PASSWORD_BCRYPT), 7]);
$affected = $stmt->rowCount();

// 綁定值/參數 (進階場景)
$stmt = $pdo->prepare('SELECT * FROM `user` WHERE `login` LIKE :pattern');
$stmt->bindValue(':pattern', 'kevin%', PDO::PARAM_STR);
$stmt->execute();

3. 刪除/更新 (無結果集)


$count = $pdo->exec("DELETE FROM `menu` WHERE `mid` = 43"); // 直接執行
// 或預處理
$stmt = $pdo->prepare('DELETE FROM `menu` WHERE `mid` = ?');
$stmt->execute([43]);
$count = $stmt->rowCount();

四、事務控制 (ACID 保證)


try {
    $pdo->beginTransaction();
    
    $pdo->exec("INSERT INTO `account` (`user`, `amount`) VALUES ('A', 100)");
    $pdo->exec("INSERT INTO `account` (`user`, `amount`) VALUES ('B', -100)");
    // 模擬業務邏輯失敗
    // throw new Exception('Insufficient balance');
    
    $pdo->commit();
    echo "Transaction committed";
} catch (Exception $e) {
    $pdo->rollBack();
    echo "Transaction rolled back: " . $e->getMessage();
}

關鍵點:

  • beginTransaction() 關閉自動提交;
  • 異常拋出時自動回滾 (若未捕獲);
  • 腳本結束/連接關閉時未提交事務自動回滾 (安全網);
  • 不支持事務的引擎 (MyISAM) 調用 beginTransaction() 會拋出異常。

五、錯誤處理三種模式

模式常量行為適用場景
PDO::ERRMODE_SILENT (默認)設置錯誤碼,需手動調用 errorCode()/errorInfo()極少用,不推薦
PDO::ERRMODE_WARNING觸發 E_WARNING,腳本繼續執行調試階段快速定位
PDO::ERRMODE_EXCEPTION拋出 PDOException,含 SQLSTATE、驅動錯誤碼、錯誤信息生產環境強制推薦

// 異常對象常用屬性/方法
$e->getMessage();      // 錯誤描述
$e->getCode();         // SQLSTATE
$e->getFile();         // 發生文件
$e->getLine();         // 發生行號
$e->getTraceAsString(); // 堆棧追蹤

六、PDO 常用屬性常量速查

分類常量說明
獲取模式PDO::FETCH_ASSOC關聯數組 (鍵=列名)
PDO::FETCH_NUM數字索引數組
PDO::FETCH_BOTH雙模式 (默認)
PDO::FETCH_OBJ匿名對象 (屬性=列名)
PDO::FETCH_CLASS映射到指定類
PDO::FETCH_LAZY延遲加載 (PDORow 對象)
連接屬性PDO::ATTR_PERSISTENT持久連接
PDO::ATTR_ERRMODE錯誤模式
PDO::ATTR_DEFAULT_FETCH_MODE默認獲取模式
PDO::ATTR_EMULATE_PREPARES模擬預處理 (false=原生)
參數類型PDO::PARAM_STR字符串
PDO::PARAM_INT整數
PDO::PARAM_BOOL布爾

七、完整業務示例:用戶註冊 + 事務 + 異常處理


 PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
]);

function registerUser(PDO $pdo, string $email, string $plainPass): int {
    $pdo->beginTransaction();
    try {
        // 1. 檢查郵箱唯一
        $stmt = $pdo->prepare('SELECT 1 FROM `users` WHERE `email` = ?');
        $stmt->execute([$email]);
        if ($stmt->fetch()) {
            throw new DomainException('Email already registered');
        }
        
        // 2. 插入用戶
        $hash = password_hash($plainPass, PASSWORD_BCRYPT);
        $stmt = $pdo->prepare('INSERT INTO `users` (`email`, `password_hash`, `created_at`) VALUES (?, ?, NOW())');
        $stmt->execute([$email, $hash]);
        $userId = (int)$pdo->lastInsertId();
        
        // 3. 初始化用戶資源 (同一事務)
        $pdo->prepare('INSERT INTO `user_profile` (`user_id`, `nickname`) VALUES (?, ?)')
            ->execute([$userId, substr($email, 0, strpos($email, '@'))]);
        $pdo->prepare('INSERT INTO `user_wallet` (`user_id`, `balance`) VALUES (?, 0)')
            ->execute([$userId]);
        
        $pdo->commit();
        return $userId;
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e; // 向上拋出,由控制器統一處理
    }
}

// 控制器調用
try {
    $uid = registerUser($pdo, 'test@example.com', 'secret123');
    echo "User created: $uid";
} catch (DomainException $e) {
    http_response_code(409);
    echo $e->getMessage();
} catch (PDOException $e) {
    http_response_code(500);
    error_log($e->getMessage());
    echo "System error, please try later";
}
?>

八、避坑指南

  1. 字符集:DSN 必須帶 charset=utf8mb4,否則存 Emoji 會截斷/報錯。
  2. 原生預處理:ATTR_EMULATE_PREPARES => false 強制使用 MySQL 原生預處理,避免邊界注入風險。
  3. lastInsertId() 陷阱:批量 INSERT ... VALUES (),(),() 僅返回第一行 ID,需分批插入或查詢 MAX(id)。
  4. fetchColumn() 陷阱:每次調用自動移動到下一行,**不可**用於同一行多列提取。
  5. 長連接風險:ATTR_PERSISTENT 可能導致「MySQL server has gone away」,需配合 ATTR_AUTOCOMMIT、連接池心跳檢測。
  6. 調試 SQL:$stmt->debugDumpParams() 輸出綁定參數與 SQL,開發環境開啟。

PDO 以「統一介面、預處理安全、物件導向」三大優勢成為 PHP 數據庫開發標準。掌握異常模式、事務邊界、預處理參數綁定,即可編寫健壯、安全、可維護的數據訪問層代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved