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 Quote Link to comment 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? Quote Link to comment 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. Quote Link to comment 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. Quote Link to comment 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? Quote Link to comment 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). Quote Link to comment 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. Quote Link to comment 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 Quote Link to comment 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. Quote Link to comment 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. Quote Link to comment Share on other sites More sharing options...
corbin Posted October 25, 2008 Share Posted October 25, 2008 Ahhhhhhhh.... I see what ya mean. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.