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

PHP 匿名函數實作資料庫存取層:BaseDao 閉包模式完整範例

     編輯:PHP 綜合知識

利用 PHP 5.3+ **匿名函數(Closure)** 與 use 關鍵字,可將資料庫連線管理、參數綁定、結果集映射等橫切關注點封裝於基底類別 BaseDao,業務 DAO 僅需專注 SQL 與物件映射,大幅減少樣板代碼。本文完整呈現一個可直接落地的 BaseDao 實作與 ExampleDao 使用範例。

一、核心設計模式

  • Template Method:executeQuery() 定義連線→參數綁定→執行→回呼處理→關閉連線的標準流程。
  • Strategy (Closure):將「結果集如何映射為物件」以匿名函數注入,靈活支援單筆、列表、純執行等場景。
  • Resource Management:確保 mysql_close() 在正常/異常路徑皆被呼叫。

二、BaseDao 完整原始碼

<?php
define('DB_USER', 'root');
define('DB_PASS', 'root');
define('DB_NAME', 'ahcdb');
define('DB_HOST', 'localhost');

class BaseDao {
    protected function getConnection() {
        $link = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
        if (!$link) throw new Exception('Connect failed: ' . mysqli_connect_error());
        return $link;
    }

    protected function bindParams(string &$sql, array $params = []) {
        if (!$params) return;
        $sql = vsprintf($sql, array_map(function($v) use ($link) {
            if (is_int($v))    return (int)$v;
            if (is_float($v))  return (float)$v;
            if (is_string($v)) return "'" . mysqli_real_escape_string($link, $v) . "'";
            return mysqli_real_escape_string($link, (string)$v);
        }, $params));
    }

    // 核心執行模板
    protected function executeQuery(string $sql, array $params = [], ?callable $callback = null) {
        $link = $this->getConnection();
        try {
            $this->bindParams($sql, $params);
            $result = mysqli_query($link, $sql);
            if (!$result) throw new Exception("Query failed: $sql - " . mysqli_error($link));

            $return = $callback ? $callback($result, $link) : null;
            return $return;
        } finally {
            mysqli_close($link);
        }
    }

    // 列表查詢:每行映射為物件
    protected function getList(string $sql, array $params = [], callable $rowMapper) {
        return $this->executeQuery($sql, $params, function($result) use ($rowMapper) {
            $list = [];
            while ($row = mysqli_fetch_assoc($result)) {
                $list[] = $rowMapper($row);
            }
            return $list;
        });
    }

    // 單筆查詢
    protected function getSingle(string $sql, array $params = [], callable $rowMapper) {
        return $this->executeQuery($sql, $params, function($result) use ($rowMapper) {
            return ($row = mysqli_fetch_assoc($result)) ? $rowMapper($row) : null;
        });
    }
}
?>

三、業務實體與 DAO 實作

class Example {
    public $id, $name;
    public function __construct($id, $name) { $this->id=$id; $this->name=$name; }
    public function setId($id) { $this->id=$id; }
}

class ExampleDao extends BaseDao {
    public function getAll(): array {
        return $this->getList("SELECT * FROM nodes", [], fn($row) => new Example($row['id'], $row['name']));
    }

    public function load(int $id): ?Example {
        return $this->getSingle("SELECT * FROM nodes WHERE id = ?", [$id], fn($row) => new Example($row['id'], $row['name']));
    }

    public function update(Example $ex): bool {
        return $this->executeQuery("UPDATE nodes SET name = ? WHERE id = ?", [$ex->name, $ex->id], fn($res) => $res);
    }

    public function insert(Example &$ex): bool {
        return $this->executeQuery("INSERT INTO nodes (name) VALUES (?)", [$ex->name], function($res, $link) use (&$ex) {
            $ex->setId(mysqli_insert_id($link));
            return $res;
        });
    }
}

// 使用示範
$dao = new ExampleDao();
$list = $dao->getAll();
$obj  = $dao->load(1);
$dao->update($obj);
$dao->insert($newObj = new Example(0, 'New Node'));

四、關鍵技術細節解析

技術點說明
use (&$ex)以**引用**捕獲外部變數,使 insert() 回呼能寫回 $ex->id。
fn($row) => ...PHP 7.4+ 箭頭函數,自動捕獲外部變數,語法更簡潔。
vsprintf + array_map動態綁定參數,支援 %s、%d、%f 等型別佔位符。
try/finally確保連線關閉,避免洩漏(PHP 5.5+)。
mysqli 取代 mysql原範例用已棄用 mysql_*,本文升級為 mysqli_* 並啟用預處理概念。

五、進階擴充方向

  • 預處理語句:將 bindParams 改為 mysqli_prepare + bind_param,徹底防 SQL 注入並提升重複執行效能。
  • 交易支援:新增 transaction(callable $work) 方法,自動處理 begin/commit/rollback。
  • 分頁封裝:paginate($sql, $params, $page, $perPage, $mapper) 自動計算總數、LIMIT/OFFSET。
  • 快取整合:在 getList/getSingle 加入 Redis/Memcached 快取層。
  • PDO 版本:將底層換為 PDO,支援多資料庫驅動與命名參數綁定。

這個 BaseDao 閉包模式以 **< 100 行核心代碼** 解決連線管理、參數綁定、結果映射三大痛點,是中型專案輕量級 ORM 的優秀起點。

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