I am the one and only

After previously explaining how to harden PDO I’m going to expand on the basic class I developed with the help of some design patterns.

A large part of using design patterns lies in recognising the situations in which each one should be used. The temptation is to implement the safePDO database class as a singleton so that only one instance of the object is used and extra connections are not made. An issue arises immediately as the constructor must be defined as public – the same as the parent PDO class – and therefore it will be able to be instantiated from anywhere. Besides, occasionally, you may also need to connect to another database which renders the singleton pattern useless.

A better choice for this situation, in my opinion, is the flyweight pattern. This is ordinarily used where a large number of similar items are to be used but can easily be applied in this situation so that database connection requests that have the same credentials can be reused.

In the following code a singleton factory pattern is used to handle and distribute requests for database connections, using the dsn and username as an array key for a pool of safePDO instances. You can rest assured that the database connection is wrapped in a try/catch block. As an added bonus, as all safePDO construction should be made from the factory class, we can make a check for this using a debug backtrace and therefore prevent a safePDO object from being instantiated at random.

If PHP code isn’t your thing – and assuming you’ve not already stopped reading by now – then you may want to give the code after the jump a miss. If your curiosity is piqued, read on . . .

[sourcecode language=’php’]< ?php Class safePDO_Flyweight extends PDO { public static function exception_handler($exception) { // Output the exception details die('Uncaught exception: ', $exception->getMessage());
}

public function __construct($dsn, $username=”, $password=”, $driver_options=array()) {

// Temporarily change the PHP exception handler while we . . .
set_exception_handler(array(__CLASS__, ‘exception_handler’));

// Find out who tried to call the constructor
$chain = debug_backtrace();
$caller = $chain[1][‘class’];

// Enforce safePDO_Factory creation
if (‘safePDO_Factory’ == $caller) {

// . . . create a PDO object
parent::__construct($dsn, $username, $password, $driver_options);

} else {
throw new Exception(‘Cannot directly instantiate. Please use safePDO_Factory’);
}

// Change the exception handler back to whatever it was before
restore_exception_handler();
}

}

class safePDO_Factory {

// Hold an instance of the class
private static $instance;

// An array of previously created safePDO connections
private $connections;

// Prevent direct creation of object
private function __construct() {
$this->connections = array();
}

// The singleton method
final public static function getInstance($dsn, $username=”, $password=”, $driver_options=array()) {
$key = $dsn . ‘.’ . $username;

if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}

// Create a new connection if we haven’t encountered it before
if (false === self::$instance->_check($key)) {
self::$instance->_add($key, $dsn, $username, $password, $driver_options);
}

// Return the connection
return self::$instance->_use($key);
}

// Check if a given key already corresponds to a safePDO connection
private function _check($key) {
return (in_array($key, array_keys($this->connections))) ? true : false;
}

// Add a new safePDO connection to the pool with named key
private function _add($key, $dsn, $username=”, $password=”, $driver_options=array()) {
try {
$this->connections[$key] = new safePDO_Flyweight($dsn, $username, $password, $driver_options);
} catch (PDOException $e) {
safePDO_Flyweight::exception_handler($e);
}
}

// Return a named safePDO connection
private function _use($key) {
return $this->connections[$key];
}

// Prevent singleton cloning
final public function __clone() {
throw new RuntimeException(‘Cannot clone a singleton class: ‘ . __CLASS__);
}

}

// Connect to the database with defined constants
$dbh = safePDO_Factory::getInstance(PDO_DSN, PDO_USER, PDO_PASSWORD);

// The rest of your code goes here . . .

// Destroy the database connection
$dbh = null;

?>[/sourcecode]

Related Posts: