I would like to encapsulate the handling of database connections, and maintain a loose coupling between database connections and application and data-access code. I’ll not focus on connection pooling since that can be handled at the driver level independent of the application and adapter code.
I would like to leverage the PDO interface as well as magic handling of the connections to allow for complete encapsulation of read-slaves and write-masters.
For example, I would like data access code to be as simple as
<?php $db = new DB(); // should magically use write-master $stmt = $db->prepare('INSERT INTO users (username) VALUES (?)'); $stmt->execute(array($username)); // should magically use a read-slave $stmt = $db->prepare('SELECT * FROM users'); $stmt->execute(); $users = $stmt->fetchAll(PDO::FETCH_ASSOC); ?>
I would like to simplify this even further to support magic object invocation, e.g.,
<?php $db = new DB(); $users = $db('SELECT * FROM users'); ?>
And even simpler, magic function invocation, also supporting prepared statements and bind parameters (without hassle), e.g.,
<?php $user = DB('SELECT * FROM users WHERE id = ?', array($user_id)); ?>
In php this can be accomplished with something like the following:
<?php use config\DB as conf; /** * DB adapter, supports: * * PDO interface * * encapsulate database handles * * dynamically choose read or write handle per call * * magic invocation and lazy connection loading * **/ class DB { private static $wdb = false; private static $rdb = false; /** * dynamically select write master or read slave, * by itself these functions can be used as a DB Connection Factory * * $conn = DB::connection(); **/ public static function connection($hint = false) { if ($hint and stripos($hint,'select') === 0 and stripos($hint,'last_insert_id') == false) { return self::read_slave(); } else { return self::write_master(); } } public static function write_master() { if (!self::$wdb) { self::$wdb = new \PDO(conf::WDSN, conf::WDB_USER, conf::WDB_PASSWORD); } return self::$wdb; } public static function read_slave() { if (!self::$rdb) { self::$rdb = new \PDO(conf::RDSN, conf::RDB_USER, conf::RDB_PASSWORD); } return self::$rdb; } /** * MAGIC, object invocation * * fully encapsulates connection (read vs write) handle * * supports both prepared statements and sql execution * * $db = new DB(); * $array1 = $db('SELECT * FROM foo'); * $array2 = $db('SELECT * FROM foo WHERE bar = ?', $params); **/ public function __invoke($sql, $params = false) { $conn = self::connection($sql); $stmt = $conn->prepare($sql); $params ? $stmt->execute($params) : $stmt->execute(); return $stmt->fetchAll(); } /** * MAGIC, proxy methods to appropriate PDO connection * **/ public function __call($method, $params) { $sql = false; if (stripos($method, 'query') === 0 || stripos($method, 'prepare') === 0) { $sql = $params[0]; } $conn = self::connection($sql); return call_user_func_array(array($conn,$method), $params); } } /** * MAGIC, functional invocation * * $array = DB('SELECT * FROM foo'); **/ function DB($sql, $params = false) { $db = new DB(); return $db($sql, $params); } ?>