萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

PHP MySQL 資料庫操作類:輕量級封裝與現代化改造指南

從 mysql_* 到 PDO:為什麼要重構?

原始碼使用已棄用的 mysql_* 函數(PHP 7.0 移除),且缺乏預處理、事務支援、異常處理。本文在保留原有介面風格的基礎上,提供 PDO 版現代化重構,並補充最佳實踐。

原版類別回顧 (僅供參考,勿用於生產)

class mysql {
    private $conn;
    function __construct($host,$user,$pwd,$database,$charset){
        $this->conn = mysql_connect($host,$user,$pwd);
        mysql_select_db($database);
        mysql_query('SET NAMES '.$charset);
    }
    function query($sql){ return mysql_query($sql); }
    function getRow($sql){ /* ... */ }
    function getAll($sql){ /* ... */ }
    function insert_id(){ return mysql_insert_id(); }
    function affected_rows(){ return mysql_affected_rows(); }
    function close(){ mysql_close($this->conn); }
}

PDO 現代化重構版

class Database {
    private PDO $pdo;

    public function __construct(array $config) {
        $dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}";
        $this->pdo = new PDO($dsn, $config['user'], $config['pass'], [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$config['charset']}"
        ]);
    }

    // 查詢單行
    public function getRow(string $sql, array $params = []): ?array {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetch() ?: null;
    }

    // 查詢多行
    public function getAll(string $sql, array $params = []): array {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll();
    }

    // 查詢單一值
    public function getOne(string $sql, array $params = []) {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchColumn();
    }

    // 執行增刪改 (返回影響行數)
    public function execute(string $sql, array $params = []): int {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->rowCount();
    }

    // 取得最後插入 ID
    public function lastInsertId(): string {
        return $this->pdo->lastInsertId();
    }

    // 事務支援
    public function beginTransaction(): void { $this->pdo->beginTransaction(); }
    public function commit(): void { $this->pdo->commit(); }
    public function rollBack(): void { $this->pdo->rollBack(); }

    // 關閉連線 (PDO 無需顯式關閉,析構自動處理)
    public function close(): void { $this->pdo = null; }
}

使用範例

$db = new Database([
    'host' => 'localhost',
    'user' => 'root',
    'pass' => 'password',
    'dbname' => 'myapp',
    'charset' => 'utf8mb4'
]);

// 參數化查詢,防 SQL 注入
$user = $db->getRow('SELECT * FROM users WHERE email = ? AND status = ?', [$email, 1]);

$users = $db->getAll('SELECT id, name FROM users WHERE created_at > ? ORDER BY id DESC LIMIT 10', [$since]);

// 寫入操作
$db->execute('INSERT INTO logs (msg, level) VALUES (?, ?)', [$message, 'info']);
$logId = $db->lastInsertId();

// 事務範例
try {
    $db->beginTransaction();
    $db->execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1]);
    $db->execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 2]);
    $db->commit();
} catch (Exception $e) {
    $db->rollBack();
    throw $e;
}

關鍵改進點

  • 預處理語句:所有查詢使用 ? 佔位符,徹底防注入。
  • 異常模式:ERRMODE_EXCEPTION 讓錯誤自動拋出,避免靜默失敗。
  • 關聯陣列預設:FETCH_ASSOC 直接回傳鍵值對,無需額外轉換。
  • 關閉模擬預處理:EMULATE_PREPARES = false 使用真實預處理,效能更佳。
  • 事務 API:原生支援 beginTransaction/commit/rollBack。
  • 型別提示:PHP 7.4+ 型別聲明提升可維護性。

遷移清單

  1. 將專案所有 mysql_* 呼叫替換為 Database 實例方法。
  2. 將字串拼接 SQL 改為參數化 ? 佔位符。
  3. 檢查 insert_id() → lastInsertId()、affected_rows() → execute() 回傳值。
  4. 移除手動 close() 呼叫(可保留空實作相容)。
  5. 執行完整測試套件驗證。

拋棄舊 API,擁抱 PDO,讓資料庫操作更安全、更優雅。

copyright © 萬盛學電腦網 all rights reserved