PHP PDO Singleton Class:
<?php
require_once('app/config.php'); // Require constants HOST, DATABASE, USER, PASSWORD
/*
dbConnection class.
Manages connections to and operations on the database. Call dbConnection::getInstance() to return an instance.
Prepare your statements by calling prepareQuery() on the object
Attribute list:
$instance:
> Static self instance to manage database resource
$connection:
> Holds connection resource
$sth:
> Statement handler variable. Handles SQL statements.
_______________________________________________________________________________________________________________
Method list:
getInstance():
> Creates or returns existing connection to the database
prepareQuery():
> Prepares the $sth variable for execution.
bindParameter():
> Binds parameters to the $sth variable.
numRows($query):
> Returns the number of returned from a query
runQuery():
> Executes the current statement on the database
fetchRow():
> Executes the current statement then returns an associative array
fetchObj($className, $parameters = NULL):
> Executes the current statement and returns an object of your specification. Also takes additional parameters to pass to the object's constructor.
*/
class dbConnection
{
private static $instance = NULL;
private $connection;
private $sth;
function __construct()
{
$this->connection = new PDO('mysql:host=' . HOST . ';dbname=' . DATABASE, USER, PASSWORD);
}
function getInstance()
{
if (self::$instance == NULL)
self::$instance = new dbConnection();
return self::$instance;
}
function prepareQuery($query)
{
$this->sth = $this->connection->prepare($query);
}
function bindParameter($number, $value)
{
$this->sth->bindParam($number, $value);
}
function numRows()
{
try
{
$this->sth->execute();
$count = $this->sth->rowCount();
return $count;
}
catch(PDOException $e)
{
echo __LINE__.$e->getMessage();
}
}
function runQuery()
{
try
{
$this->sth->execute() or print_r($connection->errorInfo());
}
catch(PDOException $e)
{
echo __LINE__.$e->getMessage();
}
}
function fetchRow()
{
try
{
$this->sth->setFetchMode(PDO::FETCH_ASSOC);
$this->sth->execute();
return $this->sth;
}
catch(PDOException $e)
{
echo __LINE__.$e->getMessage();
}
}
function fetchObj($className, $parameters = NULL)
{
try
{
$this->sth->setFetchMode(PDO::FETCH_CLASS, $className, $parameters);
$this->sth->execute();
return $this->sth;
}
catch(PDOException $e)
{
echo __LINE__.$e->getMessage();
}
}
}
?>
How to test singleton?
Take an object at a time, which is closed when you are finished with the object at the end.
I think you are misapplying the Singleton pattern here.
Nevertheless, testing Singletons is possible. Quoting Testing Code that uses Singletons
PHPUnit has a backup/restore mechanism for static attributes of classes.
This is yet another feature of PHPUnit that makes the testing of code that uses global state (which includes, but is not limited to, global and superglobal variables as well as static attributes of classes) easier.
Also see http://www.phpunit.de/manual/current/en/fixtures.html#fixtures.global-state
The #backupStaticAttributes annotation that is discussed in the section called “#backupStaticAttributes” can be used to control the backup and restore operations for static attributes. Alternatively, you can provide a blacklist of static attributes that are to be excluded from the backup and restore operations like this
So if you wanted to disable the backup, you'd do
class MyPdoTest extends PHPUnit_Framework_TestCase
{
protected $backupStaticAttributesBlacklist = array(
'dbConnection' => array('instance')
);
// more test code
}
Also have a look at the chapter on Database Testing
Singletons are bad news, as I have explained here.
More specifically to your question, Singletons are extremely difficult to unit test. Which is one of the reasons why they are bad news.
Use dependency-injection instead.
EDIT TO ADD:
I suppose you could write a set of unit tests where each unit test is in its own test suite and all the test suites are set to run in a separate process. That should isolate each unit test from the others.
The performance would be horrible though, and you're really better off just using a DI approach instead.
(I'm assuming you're using phpunit for your unit tests here)
Related
A couple of the options are:
$connection = {my db connection/object};
function PassedIn($connection) { ... }
function PassedByReference(&$connection) { ... }
function UsingGlobal() {
global $connection;
...
}
So, passed in, passed by reference, or using global. I'm thinking in functions that are only used within 1 project that will only have 1 database connection. If there are multiple connections, the definitely passed in or passed by reference.
I'm thining passed by reference is not needed when you are in PHP5 using an object, so then passed in or using global are the 2 possibilities.
The reason I'm asking is because I'm getting tired of always putting in $connection into my function parameters.
I use a Singleton ResourceManager class to handle stuff like DB connections and config settings through a whole app:
class ResourceManager {
private static $DB;
private static $Config;
public static function get($resource, $options = false) {
if (property_exists('ResourceManager', $resource)) {
if (empty(self::$$resource)) {
self::_init_resource($resource, $options);
}
if (!empty(self::$$resource)) {
return self::$$resource;
}
}
return null;
}
private static function _init_resource($resource, $options = null) {
if ($resource == 'DB') {
$dsn = 'mysql:host=localhost';
$username = 'my_username';
$password = 'p4ssw0rd';
try {
self::$DB = new PDO($dsn, $username, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
} elseif (class_exists($resource) && property_exists('ResourceManager', $resource)) {
self::$$resource = new $resource($options);
}
}
}
And then in functions / objects / where ever:
function doDBThingy() {
$db = ResourceManager::get('DB');
if ($db) {
$stmt = $db->prepare('SELECT * FROM `table`');
etc...
}
}
I use it to store messages, error messages and warnings, as well as global variables. There's an interesting question here on when to actually use this type of class.
Try designing your code in an object-oriented fashion. Methods that use the database should be grouped in a class, and the class instance should contain the database connection as a class variable. That way the database connection is available to the functions that need it, but it's not global.
class MyClass {
protected $_db;
public function __construct($db)
{
$this->_db = $db;
}
public function doSomething()
{
$this->_db->query(...);
}
}
I see that a lot of people have suggested some kind of static variable.
Essentially, there is very little difference between a global variable and a static variable. Except for the syntax, they have exactly the same characteristics. As such, you are gaining nothing at all, by replacing a global variable with a static variable. In most examples, there is a level of decoupling in that the static variable isn't referred directly, but rather through a static method (Eg. a singleton or static registry). While slightly better, this still has the problems of a global scope. If you ever need to use more than one database connection in your application, you're screwed. If you ever want to know which parts of your code has side-effects, you need to manually inspect the implementation. That's not stuff that will make or break your application, but it will make it harder to maintain.
I propose that you chose between one of:
Pass the instance as arguments to the functions that needs it. This is by far the simplest, and it has all the benefits of narrow scope, but it can get rather unwieldy. It is also a source for introducing dependencies, since some parts of your code may end up becoming a middleman. If that happens, go on to ..
Put the instance in the scope of the object, which has the method that needs it. Eg. if the method Foo->doStuff() needs a database connection, pass it in Foo's constructor and set it as a protected instance variable on Foo. You can still end up with some of the problems of passing in the method, but it's generally less of a problem with unwieldy constructors, than with methods. If your application gets big enough, you can use a dependency injection container to automate this.
My advice is to avoid global in the bulk of the code - it's dangerous, hard to track and will bite you.
The way that I'd do this is to have a function called getDB() which can either be at class level by way of a constructor injection or static within a common class.
So the code becomes
class SomeClass {
protected $dbc;
public function __construct($db) {
$this->dbc = $db;
}
public function getDB() {
return $this->dbc;
}
function read_something() {
$db = getDB();
$db->query();
}
}
or using a common shared class.
function read_something() {
$db = System::getDB();
$db->query();
}
No matter how much elegant system design you do, there are always a few items that are necessarily global in scope (such as DB, Session, Config), and I prefer to keep these as static methods in my System class.
Having each class require a connection via the constructor is the best way of doing this, by best I mean most reliable and isolated.
However be aware that using a common shared class to do this can impact on the ability to isolate fully the objects using it and also the ability to perform unit tests on these objects.
None of the above.
All the mysql functions take the database connection argument optionally. If you leave that argument out, the last connection by mysql_connect() is assumed.
function usingFunc() {
$connection = getConnection();
...
}
function getConnection() {
static $connectionObject = null;
if ($connectionObject == null) {
$connectionObject = connectFoo("whatever","connection","method","you","choose");
}
return $connectionObject;
}
This way, the static $connectionObject is preserved between getConnection calls.
I'm facing the following problem. I've wrote a function that create a connection object (AMQPConnection) given the required parameters. Now I want to write the corresponding unit test. I just don't know how to do it without having the RabbitMQ broker running. Here is the function in question:
public function getConnection($hostKey, array $params)
{
$connection = null;
try {
$connection = new AMQPConnection(
$params['host'],
$params['port'],
$params['username'],
$params['password'],
$params['vhost']
);
// set this server as default for next connection connectionAttempt
$this->setDefaultHostConfig($hostKey, $params);
return $connection;
} catch (\Exception $ex) {
if ($this->isAttemptExceeded()) {
return $connection;
} else {
// increment connection connectionAttempt
$this->setConnectionAttempt($this->getConnectionAttempt() + 1);
return $this->getConnection($hostKey, $params);
}
}
}
You usually don't test code like this as a Unittest since the result would more likely tell you that your server is installed correctly and not that your code is working.
Do you test if PDO returns a valid database connection?
It could make sense if you test your installation but not to test if the php c libs are working correctly.
You should add an ability to change the class being instantiated.
Add an ability to change the connector class via constructor or setter, with a default AMQPConnector class by default. Use it to create a connector object. Example
Create a mocked connector class for unit tests. Example
Use it setting the mock class via constructor or setter in tests. Example
You could also do assertions on what arguments are passed to the connector constructor.
Another way would be to use amqp interop so you are not coupled to any implementation. Much easier to tests as you deal with pure interfaces only.
#chozilla
#zerkms
So thanks to your hints, I've decided to use Closure in order to isolate the code portion connecting to the server.
Here is the closure:
$connectionFunction = function ($params) {
return new AMQPStreamConnection(
$params['host'],
$params['port'],
$params['username'],
$params['password'],
$params['vhost']
);
};
And here the modified getConnection() function
/**
* #param string $hostKey The array key of the host connection parameter set
* #param array $params The connection parameters set
* #return null|AMQPStreamConnection
*/
public function getConnection($hostKey, array $params)
{
$connection = null;
try {
$connection = call_user_func($connectionFunction, $params);
// set this server as default for next connection connectionAttempt
$this->setDefaultHostConfig($hostKey, $params);
return $connection;
} catch (\Exception $ex) {
if ($this->isAttemptExceeded()) {
return $connection;
} else {
// increment connection connectionAttempt
$this->setConnectionAttempt($this->getConnectionAttempt() + 1);
return $this->getConnection($hostKey, $params);
}
}
}
For the unit test I did the following:
$mockConnection = $this->getMockBuilder('PhpAmqpLib\Connection\AMQPStreamConnection')
->disableOriginalConstructor()
->getMock();
$connectionFunction = function ($params) use ($mockConnection) {
return $mockConnection;
};
Or this to have an Exception.
$connectionFunction = function ($params) {
throw new \Exception;
};
N.B.: I'm using AMQPStreamConnection in the getConnection() function since AMQPConnection is marked as deprecated in PhpAmqpLib
I'm moving onto teaching myself OOP in PHP.
I'm creating a couple of little web apps and have followed a lot of tutorials that either create the database (using PDO) via a Singleton, or via passing the global around. I've read that these are pretty much the same thing and are both to be avoided like the plague.
So I've watched the Google Tech Talks on clean code, and read almost every SO article on dependency injection and the like. I have a couple of questions.
The clean code videos suggest you shouldn't do 'work' in your constructors. Is this 'work' in reference to business logic. Ie. If my class's job is to create another object, is that an OK kind of 'work'?
For example, in trying to conform to single repsonibility classes I created three.
Class DB - which actually connects to the database.
Class DBFactory - which creates the DB object which connects to the database.
Class DBInstance - which returns a single instance of the DBFactory created PDO object.
Please note that I'm trying to create a single instance, without creating a Singleton pattern.
So I try and pass my dependencies for each class up the chain. I find myself in a position where I have to create all of the objects (from DB down) so I can inject the dependencies. For some reason I thought it would work the other way, I'd create the first object, which would create the second for me etc. I'm clearly missing something?
Hopefully this helps others as well - there seems to be a myriad of questions relating to this stuff and databases but very little good examples.
(I should mention this does work, I do get a list of hotel names out of the database!)
TestCode.php
include './classes/DB.php';
include './classes/DBFactory.php';
include './classes/DBInstance.php';
include './classes/Location.php';
$db = new DB;
$dbfactory = new DBFactory($db);
$dbinstance = new DBInstance($dbfactory);
$dbh = $dbinstance->getDbInstance();
//Example business logic
$location_names = Location::getLocationNames($dbh);
print_r($location_names);
Class DB.php:
class DB {
private $_dbhost = 'myhost';
private $_dbname = 'myname';
private $_dbuser = 'myuser';
private $_dbpass = 'mypass';
private $_error;
public function connect() {
try {
return new PDO("mysql:host=$this->_dbhost;dbname=$this->_dbname",
$this->_dbuser, $this->_dbpass);
}
catch (PDOException $e) {
$this->_error = 'Error! ' . $e->getMessage() . '<br />';
die();
}
}
public function getError() {
if (isset($this->_error)) {
return $this->_error;
}
}
}
Class DBFactory.php
class DBFactory {
private $_dbh;
public function __construct(DB $db) {
$this->_dbh = $db;
}
public function Create() {
return $this->_dbh->Connect();
}
}
Class DBInstance.php
class DBInstance {
private static $_dbinstance;
public function __construct(DBFactory $dbfactory) {
if (!isset(self::$_dbinstance)) {
self::$_dbinstance = $dbfactory->Create();
}
}
public function getDbInstance() {
return self::$_dbinstance;
}
}
Your code seems to do what you want it to.. but maybe we can use less object instantiation using inheritance and maybe we can avoid static properties in instanciated classes.
Also in regard to using a pattern of dependency injection that is able to handle multiple connections, but support using a single instance of it. exemple first, classes after
$params = array
('host'=>'localhost',
'db'=>'ice',
'user'=>'kopitar',
'pass'=>'topnet',
'charset'=>'utf8'); // passing the charset explicitely is great
$handle = new handle($params);
$db = $handle->getInstance();
we can either pass the $db to our functions
$location_names = Location::getLocationNames($db);
or the whole $handle. as long as $handle is not reconstructed, it will always return the same database connection.
$location_names = Location::getLocationNames($handle);
if I want to reconstruct I need the whole $handle
$handle->__construct(/* params but with another database infos */);
$db2 = $handle->getInstance();
As for the classes, I think we want the params to arrive from the instanciated class, so we can change them later.
class db {
function __construct($params) {
foreach ($params as $param => $value) {
$this->{$param} = $value; // assigns the connections infos
}
}
protected function connect() {
$dsn = 'mysql:host='.$this->host.';dbname='.$this->db.';charset='.$this->charset;
return new PDO($dsn,$this->user,$this->pass);
}
}
the factory creates a connection from params and passes it to something else, good factory
class factory extends db {
protected function create() {
return $this->connect();
}
}
now we want to have our object to keep it's connection as long as we do not rebuild it. so we give it to instance
class instance extends factory {
function instantiate() {
$this->instance = $this->create();
}
}
and last but not least, our handle which returns the instance. it could be in instance class.....................
but I feel like having four and find no real reason not to.
class handle extends instance {
function __construct($params) {
db::__construct($params);
$this->instantiate(); // when we construct a handle, we assign an instance to the instance property
}
function getInstance() {
return $this->instance;
}
}
KISS
Don't make things more complex than they are, of course this is just my opinion, but as I see it you are building a complex solution for a problem that someone else says might exist is some cases.
Php is not multi threaded so there goes one of the biggest arguments overboard. (in very rare-occasions it might be)
I'm using singletons for my database connections for about 15 years now and never ever had a problem with them, I do play around with different connections having one singleton handle several connection instances, but whatever... it works great and everyone that looks at the code.. understands it directly.
I'm not using globals because they can be overwritten and are kind of hard to predict (when it holds the correct object, and when/why they don't)
Use OOP to make your code cleaner, easier to work with and more flexible.
Don't use it to fix problems that aren't there and make your code more complex because others tell you to.
An very simple example of a db-connection singleton class handling several different connections.
class singleton{
private static $_instances=array();
public static function getInstance($connectionName){
if(!isset(self::$_instance[$connectionName]){
self::$_instance[$connectionName]=self::_getConnection($connectionName);
}
return self::$_instance[$connectionName];
}
}
just my 2 cents
Why do you have a factory if you have a singleton? This is needless.
This is a never-ending debate, but I'm advocate of do not use singletons for database connections.
As far as in most applications, you have only one data channel, you can consider your database connection unique, but this might not be always true.
In deed, the effort made to create a singleton database connection is even bigger than just create a regular one.
Also, your class DB is not configurable, therefore, you need to change it when your connection parameters change. And I think DB is a very bad name for this.
I'd rather call this Storage and do something like:
inteface Storage {
public function insert($container, array $data);
public function update($container, array $data, $where);
public function delete($container, $where);
public function getAll($container);
public function getOne($identifier);
}
final class PdoStorage implements Storage {
private $dbh;
private $dsn;
private $user;
private $pswd;
public function __construct($dsn, $user, $pswd) {
$this->dsn = $dsn;
$this->user = $user;
$this->pswd = $pswd;
}
// Lazy Initialization
private function connect() {
if ($this->dbh === null)
$this->dbh = new PDO($this->dsn, $this->user, $this->pswd);
}
public function insert($container, array $data) {
$this->connect();
// ... omitted for brevity
}
}
Now, when you need a database storage, you do:
$someObj = new SomeClass(new PdoStorage(...));
Now you might be wondering if you will need to create an PdoStorage for each single object that depends on it.
The answer is: no!
Now you can use a factory to simplify your life.
class SomeFactory {
private $defaultStorage;
public function __construct(Storage $storage) {
$this->defaultStorage = $storage;
}
public function create($type) {
// Somehow fetches the correct class to instantiate and put it into $class variable , for example... and then
return new $class($this->defaultStorage); // Or you'd better do this with reflection
}
}
$factory = new SomeFactory(new PdoStorage(...));
$factory->create('SomeClass');
This way, you can have just one database connector or more if you need.
I know the title doesn't give much clues about what I'm asking for, so here's the simplified situation:
class MyPDO extends PDO
{
private $stmt;
function __construct($dsn...)
{
parent:__construct($dsn...);
}
function myQuery($sql)
{
$this->stmt = $this->query($query);
}
function myFetchAll()
{
return $this->stmt->fetchAll($mode);
}
function myFetchRow()
{
return $this->stmt->fetch();
}
}
Throughout the application I have a base instance of MyPDO and pass it to different objects, mappers.
$adapter = new MyPDO($dsn...);
$adapter->myQuery('SELECT * FROM table');
$rows = $adapter->myFetchAll();
$another_object = new ObjectThatNeedsPDO($adapter);
$another_object->adapter->myQuery('SELECT * from another_table');
$rows = $another_object->adapter->myFetchAll();
Is this approach safe, especially from the MyPDO::stmt perspective? Can the application flow mess things up so I can end up fetching data from another $stmt than expected?
Personally, I would not take your approach. The reason being that I would not want the possibility of a statement object created by one class being exposed to another unrelated class. Additionally, each implementing class may have different sorts of parameter binding which it needs to perform, ways in which it needs to access the data (i.e. fetch all rows, fetch each row, fetch as objects vs. arrays, etc.), ways in which to handle errors in a class-specific way, and so forth.
To me you are gaining nothing by having this logic inside of some child of the base PDO class. I mean is it really much harder to do:
$stmt = $this->pdo->query(...)
$data = $stmt->fetchAll();
than:
$this->myPDO->query(...);
$data = $this->myPDO->myFetchAll();
What do you gain other than a unnecessary coupling of this additional class to all the classes which will consume it? Really, the statement interactions are always going to be very class specific with really the only common functionality (the DB connection) being provided by the base PDO instance.
As such, certainly feel free to pass around a common PDO instance amongst classes, this is definitely a good practice (i.e. dependency injection).
Just think really hard about whether you want to potentially change every single implementing class when you make changes to your proposed myPDO class, or whether you want to change your myPDO class every time some implementing class needs some custom means to interact with the statement object.
Based on discussions below, it seems you may want to consider extending PDOStatement to give you the maximum flexibility.
This might look like this:
class myPDOFactory {
public static function getInstance($dsn, $pdo_statement_class = 'myPDOStatement', $pdo_constructor_args = NULL);
$pdo = new PDO($dsn);
if (empty($pdo_statement_class)) {
$pdo_statement_class = 'PDOStatement';
}
if (empty($pdo_constructor_args) || !is_array($pdo_constructor_args)) {
$pdo_constructor_args = array();
}
$config_array = array($pdo_statement_class, $pdo_constructor_args);
$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, $config_array);
return $pdo;
}
}
class myPDOStatement extends PDO Statement {
public function __construct(<any custom parameters you may need to have passed - items in $pdo_constructor_args from myPDOFactory class>) {
parent::__construct();
// any special stuff you want to do with any passed parameters here
}
public function fetchAll() {
// override any functionality you desire here
}
public function fetchAllObjects() {
return $this->fetchAll(PDO::FETCH_OBJ);
}
}
class someClassThatNeedsPDO {
protected $pdo = NULL;
public function __construct($pdo) {
if($pdo instanceof PDO) {
$this->pdo = $pdo;
} else {
throw new Exception('Ooops!');
}
}
public function doSomethingWithPDO() {
$stmt = $this->PDO->prepare('SELECT * FROM sometable');
$stmt = execute();
return $stmt->fetchAllObjects();
}
}
Usage example:
$pdo = myPDOFactory::getInstance($dsn, 'myPDOStatement', $constructor_args);
$consuming_class = new someClassThatNeedsPDO($pdo);
$object_array = $consuming_class->doSomethingWithPDO();
You're approach looks like a needless layer of obstraction, but this should work fine. I'd add a check in your fetch methods to ensure a valid PDOstatement exists before calling the native fetch/fetchall.
Also, I'd recommend not creating a new connection for every MyPDO object. Pass in a PDO connection.
I always use a singleton pattern when dealing with PDO (and previously with a mysqli wrapper). This means you only ever have one instance and you don't have to pass it to other classes. The way you're doing it is okay and safe, but using a singleton is better.
Making a singleton is really easy:
class myClass{
private static $instance;
public static function singleton()
{
if (!self::$instance) {
return self::$instance = new myClass();
} else {
return self::$instance;
}
}
public static function myQuery($query)
{
#Do stuff
}
}
When you create the instance, just call:
myClass::singleton()
And then if you want to use the methods, just use this:
myClass::myQuery($query)
IF you're using namespaces, you'll need to use the fully qualified name, e.g. my\namespace\myClass
Hope that makes sense!
A couple of the options are:
$connection = {my db connection/object};
function PassedIn($connection) { ... }
function PassedByReference(&$connection) { ... }
function UsingGlobal() {
global $connection;
...
}
So, passed in, passed by reference, or using global. I'm thinking in functions that are only used within 1 project that will only have 1 database connection. If there are multiple connections, the definitely passed in or passed by reference.
I'm thining passed by reference is not needed when you are in PHP5 using an object, so then passed in or using global are the 2 possibilities.
The reason I'm asking is because I'm getting tired of always putting in $connection into my function parameters.
I use a Singleton ResourceManager class to handle stuff like DB connections and config settings through a whole app:
class ResourceManager {
private static $DB;
private static $Config;
public static function get($resource, $options = false) {
if (property_exists('ResourceManager', $resource)) {
if (empty(self::$$resource)) {
self::_init_resource($resource, $options);
}
if (!empty(self::$$resource)) {
return self::$$resource;
}
}
return null;
}
private static function _init_resource($resource, $options = null) {
if ($resource == 'DB') {
$dsn = 'mysql:host=localhost';
$username = 'my_username';
$password = 'p4ssw0rd';
try {
self::$DB = new PDO($dsn, $username, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
} elseif (class_exists($resource) && property_exists('ResourceManager', $resource)) {
self::$$resource = new $resource($options);
}
}
}
And then in functions / objects / where ever:
function doDBThingy() {
$db = ResourceManager::get('DB');
if ($db) {
$stmt = $db->prepare('SELECT * FROM `table`');
etc...
}
}
I use it to store messages, error messages and warnings, as well as global variables. There's an interesting question here on when to actually use this type of class.
Try designing your code in an object-oriented fashion. Methods that use the database should be grouped in a class, and the class instance should contain the database connection as a class variable. That way the database connection is available to the functions that need it, but it's not global.
class MyClass {
protected $_db;
public function __construct($db)
{
$this->_db = $db;
}
public function doSomething()
{
$this->_db->query(...);
}
}
I see that a lot of people have suggested some kind of static variable.
Essentially, there is very little difference between a global variable and a static variable. Except for the syntax, they have exactly the same characteristics. As such, you are gaining nothing at all, by replacing a global variable with a static variable. In most examples, there is a level of decoupling in that the static variable isn't referred directly, but rather through a static method (Eg. a singleton or static registry). While slightly better, this still has the problems of a global scope. If you ever need to use more than one database connection in your application, you're screwed. If you ever want to know which parts of your code has side-effects, you need to manually inspect the implementation. That's not stuff that will make or break your application, but it will make it harder to maintain.
I propose that you chose between one of:
Pass the instance as arguments to the functions that needs it. This is by far the simplest, and it has all the benefits of narrow scope, but it can get rather unwieldy. It is also a source for introducing dependencies, since some parts of your code may end up becoming a middleman. If that happens, go on to ..
Put the instance in the scope of the object, which has the method that needs it. Eg. if the method Foo->doStuff() needs a database connection, pass it in Foo's constructor and set it as a protected instance variable on Foo. You can still end up with some of the problems of passing in the method, but it's generally less of a problem with unwieldy constructors, than with methods. If your application gets big enough, you can use a dependency injection container to automate this.
My advice is to avoid global in the bulk of the code - it's dangerous, hard to track and will bite you.
The way that I'd do this is to have a function called getDB() which can either be at class level by way of a constructor injection or static within a common class.
So the code becomes
class SomeClass {
protected $dbc;
public function __construct($db) {
$this->dbc = $db;
}
public function getDB() {
return $this->dbc;
}
function read_something() {
$db = getDB();
$db->query();
}
}
or using a common shared class.
function read_something() {
$db = System::getDB();
$db->query();
}
No matter how much elegant system design you do, there are always a few items that are necessarily global in scope (such as DB, Session, Config), and I prefer to keep these as static methods in my System class.
Having each class require a connection via the constructor is the best way of doing this, by best I mean most reliable and isolated.
However be aware that using a common shared class to do this can impact on the ability to isolate fully the objects using it and also the ability to perform unit tests on these objects.
None of the above.
All the mysql functions take the database connection argument optionally. If you leave that argument out, the last connection by mysql_connect() is assumed.
function usingFunc() {
$connection = getConnection();
...
}
function getConnection() {
static $connectionObject = null;
if ($connectionObject == null) {
$connectionObject = connectFoo("whatever","connection","method","you","choose");
}
return $connectionObject;
}
This way, the static $connectionObject is preserved between getConnection calls.