keeps21 Posted October 24, 2008 Share Posted October 24, 2008 Just a quick query for you: If you have the following code, which is a singleton to create a database connection. Where does/could this code get the database connection details from? <?php class Database { // Store the single instance of Database private static $m_pInstance; private function __construct() { ... } public static function getInstance() { if (!self::$m_pInstance) { self::$m_pInstance = new Database(); } return self::$m_pInstance; } // Prevent singleton from being cloned private function __clone() { } } ?> Cheers Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/ Share on other sites More sharing options...
corbin Posted October 24, 2008 Share Posted October 24, 2008 Many places. Global vars (ewww). A global config class (statically accessed or what ever). It could have config hard coded, so on. Oh, by the way, a DB layer shouldn't be a singleton. What if you need to connect to two DBs? Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-673805 Share on other sites More sharing options...
Liquid Fire Posted October 24, 2008 Share Posted October 24, 2008 Oh, by the way, a DB layer shouldn't be a singleton. What if you need to connect to two DBs? I disagree with this on a certain level. My database classes kinda a singleton but not really. I do store an instance of the database object(since i generally only connect to one database server), however, the constructor is public so if you needed to connect to a seperate database server, you can. Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-673929 Share on other sites More sharing options...
448191 Posted October 24, 2008 Share Posted October 24, 2008 Then it's not a Singleton. I also don't think that's the best solution for allowing multiple connections. Better would be a type safe registry. Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-673945 Share on other sites More sharing options...
Liquid Fire Posted October 24, 2008 Share Posted October 24, 2008 Then it's not a Singleton. I also don't think that's the best solution for allowing multiple connections. Better would be a type safe registry. well i did say is was kinda a singleton but not really. As what if you need to connect to two different database servers, how can you do that without multiple connections? Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674041 Share on other sites More sharing options...
448191 Posted October 24, 2008 Share Posted October 24, 2008 What I'm saying is that if you want the global/easy access that comes with a Singleton, but not the property of ensuring a single instance, there are better ways to do that. My suggestion would be a specialized Singleton Registry (i.e. DbhRegistry for objects of type Dbh, etc). Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674124 Share on other sites More sharing options...
corbin Posted October 25, 2008 Share Posted October 25, 2008 Storing an instance != singleton. My DB class does the same thing, and it's definitely not a singleton. I guess returning a cached instance is an attribute of a singleton, but in a singleton, the same cached instance is always returned, so I guess a registry is just a dynamic singleton in a sense..... Hrmmm..... lol 448191 do you mean like: $db = DhbRegistry::GetDb('db1'); Where GetDb will spawn the instance if it doesn't exist, and then it will return a reference to the instance? Just wondering because that's what my DB layer does, and I've been wondering if there's a better way. Damn just realized I mildly thread hijacked.... Atleast it's semi-ontopic. Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674134 Share on other sites More sharing options...
keeps21 Posted October 25, 2008 Author Share Posted October 25, 2008 For anyone who's interested, the article that I got the code from is here http://www.talkphp.com/advanced-php-programming/1304-how-use-singleton-design-pattern.html Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674241 Share on other sites More sharing options...
448191 Posted October 25, 2008 Share Posted October 25, 2008 @corbin Yes, something like that. Advantage over a generic Registry instance is that the return type is known. That at least addresses one of the negative properties of a Singleton Registry. Though I wouldn't create the objects on demand by the Registry. At some point, somewhere in your app, you're going to have to provide the details for connecting/constructing. Set the Registry instance then. If you prefer, you could mend a Factory to do do that. Use a Registry that is sensitive to the difference between 'add' and 'set'. Or perhaps 'set' shouldn't be supported at all (force 'remove' + 'add'). Invoking 'add' with a used key should throw an exception. @keeps21 Check out the 'tutlet' on the main site: http://www.phpfreaks.com/tutorial/design-patterns---singleton-and-singleton-registry It was written by myself, so I may sound pretentious when I say it is better than the link you gave, but I do believe it is. Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674252 Share on other sites More sharing options...
448191 Posted October 25, 2008 Share Posted October 25, 2008 Here's another tip: try a Virtual Proxy for a database handler class. Here's an example evolving around the DSN: abstract class Dbh { private $_dsn; public function __construct($dsn) { $this->_dsn = $dsn; } public function getDsn() { return $this->_dsn; } public static function factory($dsn) { /** * Parse DSN */ $impName = substr($dsn, 0, strpos($dsn, ':')); $className = ucfirst($impName) . 'Dbh'; $self = new $className($dsn); /** * Replace in Registry to prevent unnessary repeated delegation */ DbhRegistry::set($self); return $self; } public abstract function query($sql); } class MySqlDbh extends Dbh { public function query($sql) { //execute query } } class DbhVProxy extends Dbh { private $_dbh; public function query($sql) { return $this->_getDbh()->query($sql); } private function _getDbh() { if($this->_dbh === null) { $this->_dbh = Dbh::factory($this->getDsn()); } return $this->_dbh; } } class RegistryException extends Exception {} abstract class SingletonRegistry { private $_map; final protected function __construct() {} protected function doGet($key) { return $this->_map[$key]; } protected function doAdd($key, $object) { if(isset($this->_map[$key])) { throw new RegistryException("Key already used"); } return $this->_map[$key] = $object; } protected function doRemove($key) { if(!isset($this->_map[$key])) { throw new RegistryException("Key not found"); } unset($this->_map[$key]); } final private function __clone() {} } class DbhRegistry extends SingletonRegistry { private static $_instance; private $_tTable = array(); public static function getInstance() { if(self::$_instance === null) { self::$_instance = new self(); } return self::$_instance; } public static function setTranslationTable(array $tTable) { self::getInstance()->_tTable = $tTable; } public static function get($key) { $dsn = self::getInstance()->_getDsn($key); if(!$dbh = self::getInstance()->doGet($dsn)) { $dbh = new DbhVProxy($dsn); self::add($dbh); } return $dbh; } public static function set(Dbh $dbh) { try { self::add($dbh); } catch (RegistryException $e) { self::getInstance()->doRemove($dbh->getDsn()); self::add($dbh); } } public static function remove($key) { $dsn = self::getInstance()->_getDsn($key); return self::getInstance()->doRemove($dsn); } public static function add(Dbh $dbh) { return self::getInstance()->doAdd($dbh->getDsn(), $dbh); } private function _getDsn($key) { return isset($this->_tTable[$key]) ? $this->_tTable[$key] : null; } } $config = array( "somepurpose" => "mysql://user:password@myserver/mydatabase", "someotherpurpose" => "mysql://user:password@myotherserver/myotherdatabase" ); DbhRegistry::setTranslationTable($config); //..// $dbh = DbhRegistry::get("somepurpose"); var_dump($dbh); $dbh->query("SELECT foo FROM bar"); var_dump($dbh); var_dump(DbhRegistry::get("somepurpose")); $dbh = DbhRegistry::get("someotherpurpose"); var_dump($dbh); $dbh->query("SELECT foo FROM bar"); var_dump($dbh); var_dump(DbhRegistry::get("someotherpurpose")); var_dump(DbhRegistry::getInstance()); Until you actually use the database handler, the Registry will just return an empty shell. You can provide a translation table at startup, decoupling purpose from the actual DSN. If you look at the result of the dumps at bottom the inner workings should become clear to you. I believe this also addresses the OP's concern of configuration. Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674279 Share on other sites More sharing options...
corbin Posted October 25, 2008 Share Posted October 25, 2008 Ahhhhhhhh.... I see what ya mean. Link to comment https://forums.phpfreaks.com/topic/129972-singleton-pattern-for-database-connection/#findComment-674282 Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.