【PHP開発テクニック】DBの接続管理にSingletonパターンを適用しよう

PHPアプリケーションにおけるDB接続管理にデザインパターンの一つであるSingletonを適用する例を紹介します。

Singletonパターンについての概要は以下の記事で詳しく紹介しています。
https://zenn.dev/kenchang198/articles/1d1c5ce80b7460

環境:PHP 8.2.28

データベース接続とSingletonパターン

データベース接続は、Singletonパターンの代表的な適用例です。接続の初期化コストを削減し、リソースを効率的に利用するために、アプリケーション全体で単一のインスタンスを共有します。

Singletonパターンを使用したDatabaseManagerの実装

DatabaseManagerクラスの核となる部分を以下に示します:

class DatabaseManager
{
    // 唯一のインスタンス
    private static $instance = null;
    
    // データベース接続インスタンス
    private $connection = null;
    
    // コンストラクタをprivateに
    private function __construct() {
        // デフォルト設定
        $this->config = [
            'host' => 'mysql',
            'database' => 'sample_db',
            'username' => 'sample_user',
            'password' => 'sample_password'
        ];
    }
    
    // cloneを禁止
    private function __clone() {}
    
    // unserializeを禁止(PHP 8対応)
    public function __wakeup() {
        throw new Exception("Cannot unserialize a singleton.");
    }
    
    // インスタンスを取得
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    // 接続を取得(シングルトンパターンで実装)
    public function getConnection() {
        if ($this->connection === null) {
            try {
                // PDO接続の作成
                $this->connection = new PDO(
                    "mysql:host={$this->config['host']};dbname={$this->config['database']}",
                    $this->config['username'],
                    $this->config['password']
                );
            } catch (PDOException $e) {
                throw new Exception("データベース接続エラー: " . $e->getMessage());
            }
        }
        
        return $this->connection;
    }
}

DatabaseManagerの使用例

複数の場所から同じデータベース接続にアクセスする例:

// DatabaseManagerのインスタンスを取得
$dbManager = DatabaseManager::getInstance();

// 別のクラスでも同じ接続を再利用
class ProductService {
    public function getMostExpensiveProduct() {
        // どこからでも同じインスタンスにアクセス可能
        $dbManager = DatabaseManager::getInstance();
        return $dbManager->fetchOne("SELECT * FROM products ORDER BY price DESC LIMIT 1");
    }
}

Singletonパターンがデータベース接続に適している理由

  1. リソース効率: データベース接続は高コストな操作のため、一つの接続を共有することで効率化
  2. 状態の一貫性: アプリケーション全体で一貫したデータベース状態を維持
  3. 接続数の制限: データベースの同時接続数制限対策
  4. 設定の一元管理: 接続設定を一箇所で管理可能

データベース接続におけるSingletonパターンの注意点

  1. 接続の生存期間: PHPではリクエスト間で接続は維持されない
  2. 複数データベース対応: 複数DB使用時は設計拡張が必要
  3. テスト容易性: モックに置き換えが難しい場合がある
  4. スケーラビリティ: 高負荷環境では接続プールなど他のアプローチとの併用検討

ベストプラクティス

  1. 責任の分離: データベース接続とクエリ実行に専念させる
  2. シンプルなインターフェース: 必要最小限のメソッドのみ公開
  3. 柔軟な設定: 環境に応じた設定変更を可能に
  4. 適切な例外処理: エラーを明確に通知

まとめ

データベース接続はSingletonパターンの理想的な適用例です。リソース効率化と状態の一貫性を実現しながら、複数の場所から同じ接続にアクセスできます。ただし、テスト容易性や責任分離なども考慮した設計が重要です。

Follow me!