black.horizons Posted May 15, 2009 Share Posted May 15, 2009 I've been doing PHP for over 4 years now - but only lately (last 12 months) been starting to write it as OOP. I came to the conclusion this week that my code cannot be performing efficiently at all and I've tried to research how to do it better. Various people mention registries and singletons - but i've no idea what these are - even after reading about them! I don't quite understand how they work - but I think I understand their purpose - and I think their purpose is exactly what I want. I _think_ that a database class as a singleton gives an application a single point of call to a database, and drastically reduces the amount of connections. currently my db access in oop goes a little like this: class db{} //this has the mysqli->connect statement and thats all class user($db){} //construct sets $this->db = $db; //this has lots of queries that take place in this class e.g. $db_query = $this->db->mysqli->query("select * from "); //only a dummy query!! I have several class like user each one passing in the $db, and putting queries onto it. In my head that seems like a lot of memory is being used - especially as there are usually 3 or 4 classes - as well as the DB class being used on each page - I think that might result in 4 statements of $this->db = $db; this surely can't be right and singletons / registries sound like what i need. can anybody explain to me a) what a singleton is b) what a registry is and c) how to effectively implement them via short code sample so I can an idea. I've seen people writing "getInstance()" functions - again I've no idea what they are so if that can be summarised too I'd be an extremely happy person! Quote Link to comment Share on other sites More sharing options...
KevinM1 Posted May 15, 2009 Share Posted May 15, 2009 A singleton is an object with two primary characteristics: 1. It's a global 2. Only one instance of that particular singleton can exist at any one time If you've been reading these forums, you've probably read the mantra "globals are bad." Why are they bad? They're bad because they tend to break encapsulation, and they promote tight coupling of an object to the greater system it resides in. In other words, relying on globals can make code harder to debug, less modular, and just a pain to deal with in general. Raw globals can be rewritten at any time, and there's no way to ensure that the global you want is the one you're currently using. Singletons mitigate this somewhat. Their structure (which you'll see below) only allows one to exist. Also, since they're objects, you can enforce a public interface so that their internal values aren't overwritten by accident. In the most general sense, a singleton looks like: class Singleton { private static $instance; private function __construct() {} public static function getInstance() { if(!self::$instance) { self::$instance = new self(); } return self::$instance; } } Obviously, other methods can be added to this, and both getInstance and the constructor can take arguments if you need them to. Registries are somewhat different as they serve a different purpose. The whole point of a registry is for you (or the system itself) to record and store values for future use. Think of a site that allows 3rd party plugins - they need to be registered with the system in order for the system to realize they're available for use. Where does a singleton enter the picture? Registries are often represented as singletons. The reason being that you can have different types of registries (one that stores request data, one that stores references to 3rd party plugins, etc), but you most likely only want one of each of those, as any more would be redundant. And, you probably want to be able to access those registries at any time as there's no telling exactly when you'll be reading/writing to a registry, so having them globally available is desirable. I hope this helps. Quote Link to comment Share on other sites More sharing options...
DarkSuperHero Posted May 15, 2009 Share Posted May 15, 2009 Very well explain Nightslyr, that benefitted even me... :-) Cheers! I too am starting to become more active in the OO world of PHP....After quickly moving into the world of ActionScript 3.0 I have come to realize the true value of OOP Cheers! Quote Link to comment Share on other sites More sharing options...
black.horizons Posted May 15, 2009 Author Share Posted May 15, 2009 Nightslyr, thanks for the clear cut explaination. Fortunately i'm in final year of a software engineering course doing about code management and know about coupling/cohesion. would using the singleton approach be appropriate, or the must appropriate approach for a database object then? Or would this still create many connections on a single page. For obvious reasons I want to keep the connections as low as possible, whilst still accessing all the data I need. Quote Link to comment Share on other sites More sharing options...
KevinM1 Posted May 15, 2009 Share Posted May 15, 2009 Nightslyr, thanks for the clear cut explaination. Fortunately i'm in final year of a software engineering course doing about code management and know about coupling/cohesion. would using the singleton approach be appropriate, or the must appropriate approach for a database object then? Or would this still create many connections on a single page. For obvious reasons I want to keep the connections as low as possible, whilst still accessing all the data I need. Using a singleton for a db object is pretty common practice. Whenever you want to use it, you simply store a reference to it in a variable by calling getInstance: $myDBO = Database::getInstance(); $myDBO->query(/* some query */); Since there can be only one instance of any given singleton class in the system, you'll have only one connection to the database. Something to consider - db initialization. When you first instantiate your database singleton, you'll probably want to pass in the relevant database connection info. There are a few ways to do it, but I think the simplest would be to store them in an associative array, and pass that into getInstance. We can make it (and the constructor) flexible by using default argument values: private function __construct($settings = null) { if($settings) { /* store each setting in the data member variables */ } } public static function getInstance($settings = null) { if(!self::$instance) { self::$instance = new self($settings); } return self::$instance; } With this, you're not forced into passing db setting info to getInstance every time you want to manipulate the db. Instead, you'll only need to do it in the first invocation of getInstance, when the object is actually constructed. Quote Link to comment Share on other sites More sharing options...
black.horizons Posted May 15, 2009 Author Share Posted May 15, 2009 ok i think i get it. just to confirm though...the constructor in the database class where the: if($settings){} statement lies... thats where i make the initial database connection so that the class would look a little like this: class Database { private $db_connect; private function __construct($settings = null) { if($settings) { //unserialize $settings array etc first...then... $this->db_connect = new mysqli($domain, $username, $password, $db_name); } } public static function getInstance($settings = null) { if(!self::$instance) { self::$instance = new self($settings); } return self::$instance; } } just a quick aside, I tend to hold my settings hardcoded in the database class, so that i don't have to pass in "settings", is this advisable? obviously in the constructor i could test for a boolean variable so if settings==true then /* store each setting in the data member variables */ Quote Link to comment Share on other sites More sharing options...
KevinM1 Posted May 15, 2009 Share Posted May 15, 2009 Well, one of the points of OOP is to make code modular. So, you should do what you can to make it so you can plug your db class into a variety of projects. That's why I suggest passing in some form of settings data as an argument during instantiation. In most real world scenarios, info like db connection settings are read in from a separate file. That way, instead of having to muck with actual system code in order to get this class to work on separate projects, all you'd need to modify is a non-system settings file. Quote Link to comment Share on other sites More sharing options...
Daniel0 Posted May 15, 2009 Share Posted May 15, 2009 A database wrapper, though often used as an example, is in my opinion not a good candidate for using the singleton pattern. What if you later find that you need to open another connection? If that's the case then you're out of luck as you've designed your system to only support one connection. You've coded to an interface for the singleton and you would have to go through all your code to migrate to another interface. That could turn out to be very time consuming. You can't say now that you probably won't need it unless you have the ability to look into the future. One of the strengths of OOP is that you are making your code modular and reusable, but by using the singleton pattern you are enforcing a particular type of usage, which locks you in, and which couples you very tightly. Consider the differences between these methods: public function something() { $db = Database::getInstance(); // using singleton $db->doStuff(); } public function something(Database $db) // passed by argument and using type hinting { $db->doStuff(); } There is one fundamental difference between these two methods even though they do the same. The first one is limited to always using a Database. The second one can use descendants of Database. If you have another class CustomDatabase that extends Database then you could use that one in variant two, but not easily in the first variant. One of the most important design principles is that you should code to an interface, not to an implementation. The first variant of the method fails to obey this principle because it automatically assumes that it will be specifically a Database and not potentially any descendant of that. The first one can only be changed by going through the code and changing it, i.e. it's determined at compile time. The second one can be changed dynamically at run time, which design wise is much better because it means it will have a looser coupling. The singleton pattern is in my opinion way too overused. I suppose it's because it's conceptually pretty easy to comprehend and it's also easy to implement and it gives you easy access. This easy access has several quite serious drawbacks though. Just because it's a pattern doesn't mean it's a good pattern nor that you should use it. I can't think of even one good instance where a singleton would be a better option than passing an object by argument. I'm not too much of a fan of the registry pattern either. It's mainly because it's stores elements globally. It's difficult to ensure the integrity of global objects. From within a class you can't know who have touched it, and you can't know if it contains the correct value anymore. In most registry implementations people don't check values that are put in the registry. Most people just create a registry that takes all possible data. I don't like the idea of having one big hole where you dump everything into. Again, I think it's a much better idea to pass by argument. There will always be a way of writing OO code without using neither a singleton nor registry, and I think it would be better to do that. Quote Link to comment Share on other sites More sharing options...
black.horizons Posted May 15, 2009 Author Share Posted May 15, 2009 given my relatively basic php database access systems - i have currently never come across the need to have more than one connection. i'm perhaps showing naivity here - but when would you need more than one connection? Quote Link to comment Share on other sites More sharing options...
Daniel0 Posted May 15, 2009 Share Posted May 15, 2009 Copy data from one server to another. Read data from an external server without disconnecting from local server. As I said, what you need right now doesn't matter in this instance because you can't know what you need in the future. For that reason you need to design a system that doesn't lock you in. Even then there is still the coupling issue that needs to be taken into account. Quote Link to comment Share on other sites More sharing options...
Cardale Posted May 17, 2009 Share Posted May 17, 2009 good topic ...while we are on it what is the usage of registries? You can't pass the values from script to script without a database or a flat file or session etc..so why use it? This is probably a stupid question, but I never used a registry before. Quote Link to comment Share on other sites More sharing options...
Daniel0 Posted May 17, 2009 Share Posted May 17, 2009 The idea of a registry is to have a gigantic hole to dump data in and take data out of in runtime. Things like configuration information or other things that are often used throughout the entire application. It's a bad idea though. Quote Link to comment Share on other sites More sharing options...
black.horizons Posted May 17, 2009 Author Share Posted May 17, 2009 ok just to continue the naivity - _how_ would a singleton be avoided in a multi server setup. and how is a registry avoided in a multi server setup? I'm actually thinking of an application I'm starting to draw requirements for that would require a multi server setup (multi database servers, multi file servers) Quote Link to comment Share on other sites More sharing options...
Daniel0 Posted May 18, 2009 Share Posted May 18, 2009 I don't understand your question. The registry and singleton patterns have nothing to do with a multi-server setup. Quote Link to comment Share on other sites More sharing options...
black.horizons Posted May 18, 2009 Author Share Posted May 18, 2009 i took from your saying of not using singletons for databases as you may need to use more than one connection to mean that in a multi server setup it provides a lockin that you don't need - i.e. using a singleton class limits you to one connection - which is a bad thing in a multi server setup... Quote Link to comment Share on other sites More sharing options...
Daniel0 Posted May 18, 2009 Share Posted May 18, 2009 Locking you to one connection per process is a bad idea in general because you cannot know if you at a later point will need to open another connection. If that turns out to be the case then that will be impossible without extensive modification of the existing code. If you've locked yourself in then your code is not portable. Quote Link to comment Share on other sites More sharing options...
black.horizons Posted May 18, 2009 Author Share Posted May 18, 2009 ok, so how would i go about writing an OOP database class without the use of a singleton. are you suggesting that each the database object is passed into each class then and referenced that way? Quote Link to comment Share on other sites More sharing options...
Daniel0 Posted May 18, 2009 Share Posted May 18, 2009 are you suggesting that each the database object is passed into each class then and referenced that way? Yes. Quote Link to comment Share on other sites More sharing options...
DarkSuperHero Posted May 18, 2009 Share Posted May 18, 2009 You would avoid it by not even thinking about it!( :-P ) Creating something to be a Singleton or a Singleton Registry is somethign very specific that you would "go out of your way" to create, it just doesn't happen. I think you would benefit greatly by google-ing some "PHP Design Patterns". Maybe a factory pattern to generate multiple connections, with an array as parameter containing connection details... If your application calls for it, maybe a MVC (Model View Controller) pattern? Here's a link to a few more Design patterns... http://www.fluffycat.com/PHP-Design-Patterns/ 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.