as you can see I have many Database objects but its not singleton
class registry
{
private static $instance = null;
private static $objects = array();
private function __construct() { }
public function __clone() { }
public function instance()
{
if (!isset(self::$instance))
{
self::$instance = new self();
return self::$instance;
}
}
static public function set($name, $value)
{
self::$objects[$name] = $value;
}
}
By definition, you can’t have many singleton objects from one singleton class. A singleton is a design pattern when you want to restrict instantiation of a class to one object (ie a single object).
In you second post, you should declare you constructor private to be absolutely certain of a singleton. That way client code has to use your static method to instantiate the object.
In your current code,
$obj = new Database();
would allow creation of a separate Database object.
Singleton is good for DB class, use it!
Just don’t mention that you use singleton on this forum, you will be lectured by the ‘dependency injection’ fans for sure. I was just waiting to see how long it would take for the first anti-singleton post to appear, and sure enough, it did not take long.
But, it’s true, testing can be a bit difficult with static/singletons. Since you will need an extra if test mode in there. But if you don’t use it, you will have to pass the damn DB connection everywhere. And after that, the session object, the error object and so on…
So, in my projects, I have a few classes (DB, Log, Session, Messaging, Benchmark) that are static singletons. It just makes everything much simpler and less typing.
For unit testing, i just include different configuration/class files, that would create different objects.
Post about using Register Globals and you’ll have the same thing. Any non-best-practise approach will cause controversy.
Singleton isn’t exactly the evil of evils, however it has certain problems which you will certainly come across. If you use it across the whole website, there will be a point where you come to a few stumbling blocks. For example - what if you want a model to extract data from a selection of databases, based on some credentials, but the rest of the models to load from one specific one? What if you want a module to load with a slightly different configuration from the rest of the page?
Dependency injection is surprisingly similar to singleton, the only difference being that rather than the whole global application accessing the objects. I use an ‘Application’ object which I pass to each object which requires access to request information, settings or the database.
I DO have a static function which loads the default configuration, but that is used solely in the initiation phase and can be ignored by loading the objects manually.
Here’s an example using only Database and Request objects in the application class:
class Application{
protected $_Database, $_Settings, $_Request;
function getDatabase(){ return $this->_Database; }
function getRequest(){ return $this->_Request; }
function getSettings(){ return $this->_Settings; }
function setDatabase(Database $Database){ $this->_Database = $Database; }
function setRequest(Request $Request){ $this->_Request = $Request; }
function setSettings(Settings $Settings){ $this->_Settings = $Settings; }
static function getDefault(){
$Application = new Application();
$Application->setSettings(Settings::getDefault($this));
$Application->setDatabase(MySQLDatabase::getDefault($this));
$Application->setRequest(Request::getDefault($this));
}
}
class Settings{
protected $_Settings = array();
function getSetting(){
$Path = func_get_args();
$Current = $this->_Settings;
foreach($Path as $P){
if(array_key_exists($P, $Current)){
$Current = $Current[$P];
}else{
return false;
}
}
return $Current;
}
function loadFromIniFile($FileName){
if(file_exists($FileName)){
$this->_Settings = parse_ini_file($FileName, true);
}
}
static function getDefault(Application $Application){
$Settings = new Settings();
$Settings->loadFromIniFile(parse_ini_file('Configuration/Settings.ini', true));
return $Settings;
}
}
class MySQLDatabase implements Database{
protected $_PDO;
function __Construct($Host, $Database, $Name, $Password);
function select($TableName, array $Clause = array(), $OrderBy = null, $Limit = null);
function update($TableName, array $Data, array $Clause);
function insert($TableName, array $Data);
function remove($TableName, array $Clause, $Limit = null);
function getDefault(Application $Application){
$Settings = $Application->getSettings();
$Host = $Settings->getSetting('MySQL', 'Host');
$Database = $Settings->getSetting('MySQL', 'Database');
$Name = $Settings->getSetting('MySQL', 'Name');
$Password = $Settings->getSetting('MySQL', 'Password');
if($Host !== false && $Database !== false && $Name !== false && $Password !== false){
$MySQL = new MySQLDatabase($Host, $Database, $Name, $Password);
return $MySQL;
}else{
//produce error
}
}
}
class Request{
protected $_SuperGlobals, $_URI;
function getDefault(Application $Application){
//you get the point :P
}
}
You then pass $Application through the constructor of any object that needs database access etc.
The benefit comes from when you want to load a module as if it’s meant to be on its own request. You can create a clone of $Application, but change that specific Request object’s URI to request something else - within the same application as another request. This means you can load a plugin with something like:
$Clone = new Application();
$Clone->setSettings($Application->getSettings());
$Clone->setDatabase($Application->getDatabase());
$FalseRequest = new Request();
$FalseRequest->setURI('Content', 'Menu');
$FalseRequest->setSuperGlobals(array('Session' => $Application->getRequest->getSession())); //post and get default to blank arrays
$Clone->setRequest($FalseRequest);
$Menu = new ExternalModule($Clone);
That, of course, is a trivial example, but there are huge benefits to using different instantiations of objects. If you want to use singleton, you may as well not be using objects at all.
I have never used the Zend Framework - but I’ve seen in discussions that Singleton was used in older versions of the framework but not more recent - though I can’t back that up.
Can you give an example of the singleton code used with the Zend framework?