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演算子が使えてしまうしイマイチ美しくない。他にイイ方法があれば教えてください~!
TrackBack URL :
Comments (0)