Singletons in PHP
We’re going to keep this one short and oh-so-sweet. First of all, let’s take a brief moment of silence for the late, great Michael Jackson. *Sigh*
Now, on to Singletons. One of the most common assumptions I hear about the Singleton pattern is a blatant declaration to simply not use them – as if they could absolutely never possibly be useful for anything, ever. That is, of course, very lame.
In truth, it’s best to shy away from Singletons as a general rule of thumb, as their use is not all that practical in general. They have very specific goals and tasks, and do not apply in just any scenario. For instance, if we’re building a web application that is going to be database-intensive and have multiple functions simultaneously running different queries, we most definitely do NOT want new connection instances created for every single call. We might approach this by writing a database connection Singleton (and since I have not yet written any PHP code in this blog…):
1 2 3 4 5 6 7 8 9 | class Singleton { private static $instance; private function __construct () {} //Disallow public instantiation public static function getInstance () { if ( ! isset( self::$instance ) ) self::$instance = new Singleton; return self::$instance; } } |
And voila! A singleton is born. Now, for all intents and purposes, our example is effectively pointless, but you can see where I was going. Oh, you can’t? Okay, try this one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class DB_Connection { private static $instance; private $connection; private function __construct () {} public static function getInstance () { if ( ! isset( self::$instance ) ) self::$instance = new DB_Connection; return self::$instance; } public function connect ( $host, $user, $pass ) { return $this->connection = @mysql_connect( $host, $user, $pass ); } public function select_db ( $db ) { return mysql_select_db( $db, $this->resource ); } public function query ( $query ) { return mysql_query( $query, $this->resource ); } ... } |
With me now? Oh good. Now, to make things more modular, let’s create an Interface for singletons…except we can’t. Interfaces only allow public methods to be defined. That’s okay – we’ll use an abstract method…oh shit, we can’t do that either. Using an abstract class would force us to extend the class for any Singleton purposes we might have, meaning that we would still have to define any static variables for each class, as static variables are not inherited in children. Ah well. Moving on – so we have our singleton! Great. Now we have a small, tiny little problem. You see, the general concept behind singletons is not only single instances of an object, but single aliases of that object as well. Unfortunately for us, I can reference the singleton class with as many variable names as I choose, setting and unsetting as I see fit:
1 2 3 4 5 6 7 8 9 | class DB_Connection { ... } $conn1 = DB_Connection::getInstance(); $conn2 = DB_Connection::getInstance(); //References same object instance, mind you! $and_another_conn = DB_Connection::getInstance(); //References same object instance, mind you! $conn1->connect( "localhost", "john", "teh_p4ssw0rd" ); $conn2->select_db( "teh_db" ); $and_another_conn->query( "SELECT * FROM `teh_table`" ); |
Very uncool. Let’s fix that – we need to every-so-slightly modify the getInstance method:
1 2 3 4 5 6 7 8 9 10 11 | class DB_Connection { ... public static function getInstance () { if ( ! isset( self::$instance ) ) { self::$instance = new DB_Connection; return self::$instance; } throw new Exception( "Sorry, dude! DB_Connection has already been referenced!" ); } ... } |
Voila again! Now we have an actual Singleton, which, additionally, in regards to it’s aliases – and similarly to highlander – there can be only one. We could also prevent cloning of the class (which we really want to do) by overloading the __clone magic method:
1 2 3 4 5 6 7 | class DB_Connection { ... public function __clone () { throw new Exception( "Sorry again, dude! No clones allowed!" ); } ... } |
That’s it! A word to the wise: be careful with your Singletons, or else you may end up like these guys. Don’t just assume there will only ever be one instance of something unless your domain model specifically requires one instance of something. Otherwise, you’ll find yourself globally accessing variables more often than Rosie O’Donnell changes vibrators…I’m just saying.
November 20th, 2009 at 1:50 am
Good post, although I enjoyed the one on JavaScript caching more. I’m not sure I agree with your statement that a purpose of Singletons is also to have only one alias; at least in Java, it’s a common practice to have multiple declarations, as long as they reference the same object.
Either way, good post John. I’ve already bookmarked this blog and sent you out to 20 of my programming friends.
March 1st, 2010 at 1:18 pm
Hi,
Couldn’t we define an interface declaring connect and query methods to be used ? Might be useful sometimes
March 1st, 2010 at 1:33 pm
@Gerard: Thanks, I appreciate it! True, it may not be a requirement for you, and it may even make things more difficult on you as a developer, but I can definitely rationalize the need to only ever allow one alias: at the very least, it would lessen calls to getInstance((), make it easier for threading (locking only one alias), reduce references (trivial amount of memory, I know
), and maybe the most practical, clean up your code!
@Chris: Certainly, you can absolutely extract out your public methods in the DB_Connection class into an Interface, and yes, it would be helpful – for DB_Connection. However, that doesn’t really get you any wins in regards to encapsulating the logic germane to the Singleton pattern itself (#__construct and #getInstance, and the $instance field). This was what I was talking about in that it rather sucks to have to re-implement this boilerplate code every time you need a Singleton. Now, that being said, hopefully you’re not going to be using too many Singletons in your code anyway, but that’s tangential
. Thanks for the comment!
March 2nd, 2010 at 9:56 am
Thanks for the quick answer!
I actually use the Singleton classes for the Database connection, Logger, Debugger, Session manager and Current User. All those instances should be unique around the whole application so I guess this is “ok”
Any advice or comment on this ?
March 2nd, 2010 at 3:00 pm
Glad I could respond so quickly to you! Gerard might not be too pleased about my response time to him though
.
To address your statement, it’s not so much about how unique each individual singleton is, but rather about the duplication of code used to simply implement the logic behind instantiating a singleton in the first place. Consider the concept of DRY as it applies to minimizing duplication of logic and, where possible, reducing boilerplate code. While you’re database class will hold very different methods and fields than your session singleton, they both share the core logic of creating a single instance of themselves through a private constructor called by a static initializer: this is where abstraction and centralization of code would be most useful.
Does that make sense? I enjoy conversations like these