patternphpMinor
A PDO connection classto initiate multiple connection and easy access
Viewed 0 times
initiatepdoeasymultipleandaccessconnectionclassto
Problem
I've been looking for a way to make the database connection easy available directry from any script, and from inside other classes. And since I tend to have different connections based on what action to execute, I had to find a way to easily scale the amount of connection needed.
Here's the solution I come up with.
I created this class:
And this script to set/initiate multiple connections:
```
if(data
Here's the solution I come up with.
I created this class:
class database{
protected static $details = array(); // holds all connection(s) details
protected static $con = array(); // holds the actual PDO connection(s)
/**
* addConnection()
* Adds a new connection
* @param string $alias a unique identifier for the connection
* @param string $db the database name
* @param string $un username
* @param string $pw password for the user
* @param string $desc a short description for this connection
*/
public static function addConnection($alias,$db,$un,$pw,$desc){
self::$details[$alias]=array('alias'=>$alias,'db'=>$db,'un'=>$un,'pw'=>$pw, 'desc'=>$desc);
}
/**
* getConnection()
* @return array of a specific connection if a valid alias is provided. Othervise return a multidimensional array of all connections.
*/
public static function getConnections($alias=''){
if(!empty($alias)) return self::$details[$alias];
else return self::$details;
}
/**
* setConnection()
* This function moves the connection to $con[] for easy access both inside and outside of this class
* @param string $alias a unique identifier for the connection
* @param string $connection the string of a new pdo connection
*/
public static function setConnection($alias,$connection){
self::$con[$alias] = $connection;
}
/**
* useConnection()
* @param string $alias a unique identifier for the connection
* @return a specific PDO connection based on the given alias
*/
public static function useConnection($alias){
return self::$con[$alias];
}
}And this script to set/initiate multiple connections:
```
if(data
Solution
Ok, first I'm going to point out a couple of stylistic issues I have with your code:
A quick note on the type-hints, in case you don't know how they work: it's really easy:
This makes life a lot easier when debugging. That, and any decent IDE will be happy to notify you of invalid arguments, without having to run the actual code code...
Be that as it may: I do understand that, in sizable projects, having access to existing DB connections can be useful, and that using straightforward dependency injection to achieve this can be tedious and sometimes error-prone.
However, seeing as I don't really know how big your project is, I will say that resorting to a sort of Registry should be kept as a last resort. Especially since your use of static properties to store the connections is very dangerous, too:
Static properties are shared across all instances and classes. Both parent and child. By making the properties protected, the child class can override them, which would constitute a breach of contract, to say the least. This is dangerous, so if you are set on using your code, I'd urge you to declare the properties as
Also be aware of things like late-static binding: you may have come across code like this:
Do you know what the output will be? Look closely at the use of
another value some value
Couple that to the fact that the Liskov principle states that the child may not restrict the inherited contract (ie: a parents
And what you end up with is a potentially dangerous:
And here, you're in trouble. Now the line of code where the
Let me paint you a scene that is quite plausible given the above situation: imagine yourself sitting at your desk, you're hungry, it's getting late, and you're stuck chasing an obscure bug that is proving rather hard to replicate...
You've ran unit tests, so it can't be the code itself, it has to be an expression or statement somewhere in the actual application. You've set up XDebug, and set breakpoints at every line of code that involves the class that is causing the problems. At no point do you see the
- USE TYPE-HINTS: your
addConnectionmethod expects aPDOinstance as second argument, a type-hint makes sure your code fails if this method is not passed the correct argument.
- Fix your doc-blocks: all of the params in your doc-blocks are denoted as being of the type
string. This is false. Like I mentioned above: if you need aPDOinstance, the method signature and the documentation should reflect this.
- Coding standards: the PHP-FIG coding standards are widely adopted, and are as close to official standards as you can get in PHP. You should adhere to them, too.
- Statics, in PHP, are essentially globals in OOP-drag. Things like Singletons are widely accepted to be bad practice. they make for hard to test code, and don't really offer you any advantage.
- You seem to have the intent of writing a sort-of wrapper around
PDO. I have been quite vocal about this here. More specifically: my distaste of such practices and the pointlessness of it all. Don't do it.
A quick note on the type-hints, in case you don't know how they work: it's really easy:
/**
* @param string $name
* @param \PDO $conn
* @return $this
*/
public function setConnection($name, PDO $conn)
{
$this->connection = $conn;
return $this;
}
//using this method:
$instance->setConnection('foobar', new PDO());//will work fine
$instance->setConnection('foobar', 'a string');//<-- fatal errorThis makes life a lot easier when debugging. That, and any decent IDE will be happy to notify you of invalid arguments, without having to run the actual code code...
Be that as it may: I do understand that, in sizable projects, having access to existing DB connections can be useful, and that using straightforward dependency injection to achieve this can be tedious and sometimes error-prone.
However, seeing as I don't really know how big your project is, I will say that resorting to a sort of Registry should be kept as a last resort. Especially since your use of static properties to store the connections is very dangerous, too:
Static properties are shared across all instances and classes. Both parent and child. By making the properties protected, the child class can override them, which would constitute a breach of contract, to say the least. This is dangerous, so if you are set on using your code, I'd urge you to declare the properties as
private and implement a couple of final protected getter functions in the parent class, to prevent any child classes from messing up your system.Also be aware of things like late-static binding: you may have come across code like this:
class Foo
{
protected static $StaticVar = 'some value';
public function doStuff()
{
return static::$StaticVar;
}
public function otherStuff()
{
return self::$StaticVar;
}
}
class Bar extends Foo
{
protected static $StaticVar = 'another value';
}
$b = new Bar;
echo $b->doStuff(), ' ', $b->otherStuff(), PHP_EOL;Do you know what the output will be? Look closely at the use of
self:: vs static::: the output is:another value some value
Couple that to the fact that the Liskov principle states that the child may not restrict the inherited contract (ie: a parents
protected property or method may not be overridden by a private one), but a child can make an inherited member more visible:class Dad
{
protected $foo = null;
}
//perfectly valid
class LegitimateSon extends Dad
{
public $foo = 'now $foo is public!';
}
class IllegitimateSon extends Dad
{//FATAL ERROR
private $foo = false;
}And what you end up with is a potentially dangerous:
class DangerousSQL extends Database
{
//note the CamelCased class name, and statics start with an Upper-case, too
public static $Con = array();//<-- overrides the protected static
//your query method...
public static function Query($string)
{}
}
DangerousSQL::$Con = 'This is not an array!';
DangerousSQL::Query('SELECT fail FROM bad.code WHERE inheritance = 1');And here, you're in trouble. Now the line of code where the
$Con property is assigned a string is in plain sight, but imagine a sizable project where this reassignment is hiding an another file, and isn't always executed. Or worse: somebody has extended the DangerousSQL class, and it's a child of this class that is causing problems... What now?Let me paint you a scene that is quite plausible given the above situation: imagine yourself sitting at your desk, you're hungry, it's getting late, and you're stuck chasing an obscure bug that is proving rather hard to replicate...
You've ran unit tests, so it can't be the code itself, it has to be an expression or statement somewhere in the actual application. You've set up XDebug, and set breakpoints at every line of code that involves the class that is causing the problems. At no point do you see the
$Con propeCode Snippets
/**
* @param string $name
* @param \PDO $conn
* @return $this
*/
public function setConnection($name, PDO $conn)
{
$this->connection = $conn;
return $this;
}
//using this method:
$instance->setConnection('foobar', new PDO());//will work fine
$instance->setConnection('foobar', 'a string');//<-- fatal errorclass Foo
{
protected static $StaticVar = 'some value';
public function doStuff()
{
return static::$StaticVar;
}
public function otherStuff()
{
return self::$StaticVar;
}
}
class Bar extends Foo
{
protected static $StaticVar = 'another value';
}
$b = new Bar;
echo $b->doStuff(), ' <=> ', $b->otherStuff(), PHP_EOL;class Dad
{
protected $foo = null;
}
//perfectly valid
class LegitimateSon extends Dad
{
public $foo = 'now $foo is public!';
}
class IllegitimateSon extends Dad
{//FATAL ERROR
private $foo = false;
}class DangerousSQL extends Database
{
//note the CamelCased class name, and statics start with an Upper-case, too
public static $Con = array();//<-- overrides the protected static
//your query method...
public static function Query($string)
{}
}
DangerousSQL::$Con = 'This is not an array!';
DangerousSQL::Query('SELECT fail FROM bad.code WHERE inheritance = 1');# config.yml
databases:
alias:
dsn: 'mysql:host=127.0.0.1;dbname=foobar'
user: 'username'
pass: 'passwd'
attr:
PDO::ATTR_EMULATE_PREPARES: false
PDO::ATTR_ERRMODE: PDO::ERRMODE_EXCEPTIONContext
StackExchange Code Review Q#64329, answer score: 2
Revisions (0)
No revisions yet.