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

PHP 單例模式實作教學

     編輯:PHP 綜合知識

什麼是單例模式?

單例模式確保某個類別在全域僅有一個實例,並提供全域存取點。適用於資料庫連線、配置管理、日誌記錄器等「全域唯一」資源。

經典實作(PHP 5.3+)

<?php
class Database {
    private static ?self $instance = null;
    private PDO $pdo;

    // 私有建構子,防止外部 new
    private function __construct() {
        $this->pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'user', 'pass');
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    // 禁止克隆
    private function __clone() {}

    // 禁止反序列化
    public function __wakeup() {
        throw new Exception('Cannot unserialize singleton');
    }

    // 靜態工廠方法
    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // 業務方法
    public function query(string $sql, array $params = []): PDOStatement {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }
}

// === 使用方式 ===
$db = Database::getInstance();
$users = $db->query('SELECT * FROM users WHERE status = ?', [1])->fetchAll();

// 任何地方呼叫 getInstance() 都得到同一實例
$db2 = Database::getInstance();
var_dump($db === $db2); // true
?>

關鍵要素檢查表

要素實作方式目的
私有靜態屬性儲存實例private static ?self $instance = null;保存唯一實例參考
私有建構子private function __construct()阻止 new Class()
私有 __cloneprivate function __clone()阻止 clone $obj
私有 __wakeuppublic function __wakeup() { throw ... }阻止反序列化產生新實例
靜態工廠方法public static function getInstance()提供全域存取點

進階:支援多種配置的「多例」模式

若需根據不同 DSN 建立多個單例(如主庫/從庫),可改用陣列儲存:

<?php
class Database {
    private static array $instances = [];
    private PDO $pdo;

    private function __construct(string $dsn, string $user, string $pass) {
        $this->pdo = new PDO($dsn, $user, $pass);
    }

    public static function getInstance(string $name = 'default', string $dsn = '', string $user = '', string $pass = ''): self {
        if (!isset(self::$instances[$name])) {
            if (!$dsn) throw new InvalidArgumentException("首次建立 '$name' 需提供 DSN");
            self::$instances[$name] = new self($dsn, $user, $pass);
        }
        return self::$instances[$name];
    }
    // ... query 方法同前
}

// 使用
$master = Database::getInstance('master', 'mysql:host=10.0.0.1;dbname=app', 'rw', 'secret');
$slave  = Database::getInstance('slave',  'mysql:host=10.0.0.2;dbname=app', 'ro', 'secret');
// 再次取得
$master2 = Database::getInstance('master'); // 參數可省略
?>

現代替代方案:依賴注入容器

在 Laravel、Symfony、ThinkPHP 等現代框架中,**單例由容器管理**,類別本身不需實作單例介面:

<?php
// Laravel 服務提供者註冊
$this->app->singleton(Database::class, function ($app) {
    return new Database(config('database.connections.mysql'));
});

// 控制器中型別提示自動注入
class UserController {
    public function index(Database $db) {
        return $db->query('SELECT * FROM users');
    }
}
?>

優點:解耦、易測試(可注入 Mock)、支援多實例、生命週期由容器統一控制。

何時使用單例?

  • ✅ 真正的全域共享資源(資料庫連線池、配置快取、日誌器)
  • ✅ 歷史專案無容器、快速引入
  • ❌ 業務邏輯類別(UserService、OrderService)— 應由容器管理生命週期
  • ❌ 需並行不同配置的場景 — 改用多例或容器

單例模式簡單強大,但濫用會導致強耦合、難以單元測試。現代開發優先選擇依賴注入容器管理物件生命週期。

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