どもです。
今回は最近の悩みについて。
プロジェクトを1から立ち上げるとき、共用のクラスをどうするかが問題になる。
だいたいはstatic関数にしてしまうか都度newするかで解決するのだけど、データベースを扱うクラスだけは簡単にいかない。
●前提
・このプロジェクトで使用するデータベースは1つのみであり、データベース情報は定数で宣言する。
・DBクラスはプログラムの開始時に一度だけ宣言し、全てのクラスで同じインスタンスを使いまわす。
・DB操作はmysqli関数を使用する。(PDOでも結論は同じだが)
class DB{
protected $mysql = null;
function __construct(){
$this->mysql = null;
if($this->mysql = mysqli_connect(host, user, pass)) {
if(!mysqli_select_db($this->mysql, name)){
throw new \Exception("データベースへの接続に失敗しました。");
}
}
}
function query($sql){
return mysqli_query($this->mysql, $sql);
}
}①グローバル変数にする
$db = new DB();
class test{
function a($sql){
$GLOBALS['db']->query($sql);
}
}ggった場合に出てくる解決法はだいたいグローバル化だが、クラスの中で使用するとこうなるのでスマートとは言い難い。
あとグローバル変数は使うな勢力が幅を利かせている昨今、このやり方は文句を言われそうだ。
②エントリーポイントで取得したインスタンスを処理クラスに渡す
class Main{
function __construct(){
$db = new DB();
$test = new test($db);
$test->a($sql);
}
}
class test{
function __construct($db){
$this->db = $db;
}
function a($sql){
$this->db->query($sql);
}
}次に思いつくのは、共通処理の最初に宣言したインスタンスを処理クラスのコンストラクトに手渡しする手段。
しかし、こちらもスマートと言えたものではない。明示的に手渡ししたクラスでしかDBが使えないから拡張性に欠けるし、コンストラクタがぐちゃぐちゃするのは個人的に嫌いだ。
③DBクラスをシングルトンにする
class DB{
protected static $mysql = null;
protected static $instance = null;
private function __construct(){
略
}
static function query($sql){
return mysqli_query(self::$mysql, $sql);
}
static function createInstance(){
if (!isset(self::$instance)) {
self::$instance = new DB();
}
}
}
class Main{
function __construct(){
DB::createInstance(); //エントリーポイントでDBを宣言
$test = new test();
$test->a($sql);
}
}
class test{
function a($sql){
DB::query($sql); //既にDB::$mysqlがあるので、個々のクラスで宣言せずとも自由に使える
}
}辿り着いたのがコレ。シングルトンという概念を知らなかったので目から鱗だった。
なるほど、DBクラスが自身の単一のインスタンスをstaticに持ってしまえば、staticで同じ接続を取り出すことができるわけだ。実にスマートだ。
④trait(多重継承) を使ってみる
trait DB{
protected $mysql = null;
function __construct(){
略
}
function query($sql){
return mysqli_query($this->mysql, $sql);
}
}
class test{
use DB;
function a($sql){
$db->query($sql);
}
}単一責任の原則のもとDBにアクセスできるクラスは限定的であるべき??
ならphp5.4以降はtraitを用いた多重継承ができるので、DBクラスを多重継承してみようか。
これなら明示的にDBをUSEしないと使えないので、単一責任の原則に従いながら要件を満たせる…
ということは無くて、この方法だとクラスごとにthread_idが変わる=新しい接続を行ってしまう。
やはりPHPプロジェクトでの単一データベースの利用はシングルトンが順当のようだ。