首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 封裝數據庫操作類:PDO 基礎封裝與最佳實踐模式

     編輯:PHP 相關知識

直接在業務代碼中寫原生 PDO/mysqli 不僅繁瑣,還易發生 SQL 注入、資源洩漏、編碼亂碼。本文提供一個生產級 PDO 封裝類,支持預處理、事務、異常處理、調試模式,並說明進階擴展方向。

核心封裝類實現

<?php
declare(strict_types=1);

class Database {
    private PDO $pdo;
    private bool $debug = false;
    private array $queryLog = [];

    public function __construct(array $config) {
        $dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset'] ?? 'utf8mb4'}";
        $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'] ?? 'utf8mb4'}",
        ]);
    }

    public function setDebug(bool $debug): self { $this->debug = $debug; return $this; }

    /** 通用查詢:自動預處理綁定參數 */
    public function query(string $sql, array $params = []): PDOStatement {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        if ($this->debug) $this->queryLog[] = ['sql' => $sql, 'params' => $params, 'time' => microtime(true)];
        return $stmt;
    }

    /** 獲取單行 */
    public function fetchOne(string $sql, array $params = []): ?array {
        return $this->query($sql, $params)->fetch() ?: null;
    }

    /** 獲取所有行 */
    public function fetchAll(string $sql, array $params = []): array {
        return $this->query($sql, $params)->fetchAll();
    }

    /** 獲取單值 */
    public function fetchColumn(string $sql, array $params = [], int $col = 0): mixed {
        return $this->query($sql, $params)->fetchColumn($col);
    }

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

    /** 返回最後插入 ID */
    public function lastInsertId(string $name = null): string {
        return $this->pdo->lastInsertId($name);
    }

    /** 事務執行:自動回滾異常 */
    public function transaction(callable $callback): mixed {
        $this->pdo->beginTransaction();
        try {
            $result = $callback($this);
            $this->pdo->commit();
            return $result;
        } catch (Throwable $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }

    /** 獲取調試日志 */
    public function getQueryLog(): array { return $this->queryLog; }
}
?>

使用示例

<?php
require 'Database.php';

$config = [
    'host' => '127.0.0.1',
    'user' => 'root',
    'pass' => 'password',
    'dbname' => 'myapp',
    'charset' => 'utf8mb4',
];

$db = new Database($config);
$db->setDebug(true); // 開發環境開啟

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

// 批量插入
$db->transaction(function($db) use ($users) {
    foreach ($users as $u) {
        $db->execute('INSERT INTO users (name, email) VALUES (?, ?)', [$u['name'], $u['email']]);
    }
});

// 調試輸出
if ($db->getQueryLog()) {
    echo '<pre>'; print_r($db->getQueryLog()); echo '</pre>';
}
?>

關鍵設計決策解析

決策點選擇理由
錯誤模式PDO::ERRMODE_EXCEPTION強制拋出異常,避免靜默失敗,配合 try-catch 統一處理
預處理模式ATTR_EMULATE_PREPARES = false使用數據庫原生預處理,防止模擬預處理導致的注入繞過
默認獲取模式FETCH_ASSOC關聯數組語義明確,避免數字索引混淆
字符集DSN + INIT_COMMAND 雙重設置確保連接、結果集、錯誤信息全鏈路 UTF-8
事務 APItransaction(callable)自動提交/回滾,業務代碼無需手動管理,異常安全

進階擴展方向

  1. 查詢構建器:集成 Doctrine DBAL QueryBuilder 或自建輕量 Builder,支持鏈式 ->table('users')->where('id', 1)->get()
  2. 讀寫分離:構造函數接受 ['write' => ..., 'read' => [...]],SELECT 隨機走從庫
  3. 連接池:Swoole/RoadRunner 環境下複用連接,配合 PDO::ATTR_PERSISTENT 謹慎使用
  4. 分表路由:query() 解析 SQL 自動路由到分表
  5. 審計日志:queryLog 定期刷入 Elasticsearch/ClickHouse,支持慢查詢告警

生產環境建議:直接使用成熟 ORM(Doctrine/Eloquent)或 Query Builder,手寫封裝僅適合極簡項目、教學或特殊定制需求。核心原理(預處理、異常模式、事務封裝)必須掌握。

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