@blog.justoneplanet.info

日々勉強

PDOでSingletonを実現しよう

■はじめはコンナ感じのコードを書いてみた

class Db extends PDO {
    private static $instance;
    public function __construct($dsn, $DB_USER, $DB_PASS){
        parent::__construct($dsn, $DB_USER, $DB_PASS);
    }
    public static function getDbh(){
        if(!isset(self::$instance)){
            $dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . '';
            self::$instance = new self($dsn, DB_USER, DB_PASS);
        }
        return self::$instance;
    }
    public final function __clone(){
    throw new RuntimeException('Clone is not allowed against ' . get_class($this));
    }
}
//$dbh = Db::getDbh();

普通にPDOを継承したクラスを作ってみたのだが、__constructをprivateにできない。何故なら、PDOのコンストラクタがpublicだから。この方法だとインスタンスのクローン化は防げるのだが、new演算子でDbクラスのインスタンスがいくつも作れてしまう。

■次にこんなのを書いてみた

class Db {
    private static $dbh;
    private static $instance;
    private function __construct(){
        $dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . '';
        self::$dbh = new PDO($dsn, DB_USER, DB_PASS);
    }
    public static function getDbh(){
        if(!isset(self::$dbh)){
            self::$instance = new self();
        }
        return self::$dbh;
    }
}
//$dbh = Db::getDbh();

この方法だとnew演算子によるDbクラスのインスタンス化は防げる。しかしclone演算子による$dbhのクローン化を防げない。何故なら__cloneを定義しても、例外が発生するのはdbのインスタンスの複製時のみであり、dbのインスタンスとdbhは別モンだからだ。

したがって、__cloneを定義するにはclass Dbがclass PDOと同一、もしくは継承してなくてはイケナイ。

■最終的に以下のようなコードになった

final class ForbiddenClonePDO extends PDO {
    public final function __clone(){
        throw new RuntimeException('Clone is not allowed against ' . get_class($this));
    }
}
class Db {
    private static $instance;
    /**
    * constructor
    * set the instance of ForbiddenClonePDO into thie member
    */
    private function __construct(){
        try {
            $dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME;
            self::$instance = new ForbiddenClonePDO($dsn, DB_USER, DB_PASS);
            self::$instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
            self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        catch(PDOException $e){
        }
    }
    /**
    * get the instance of PDO
    *
    */
    public static function getDbh(){
        if(!isset(self::$instance)){
            new self();
        }
        return self::$instance;
    }
}
//$dbh = Db::getDbh();

これでdbhは単一が保障され、クローンも生成されない。ただし、個人的には新しくForbiddenClonePDOを作ってしまったのが気になる。こちらはnew演算子が使えてしまうしイマイチ美しくない。他にイイ方法があれば教えてください~!

コメントはまだありません»

No comments yet.

RSS feed for comments on this post.TrackBack URL

Leave a comment