IntroductionTo my mind, PHP 5.3 is the most exciting thing to happen to PHP since PHP 5 itself was released. There's plenty to say about the new features and the opportunities that are now available to us, but in this article I want to get down to some good ol' fashioned coding. I'll save the eulogising for another time.
At the heart of the code I'll shortly be introducing, is a new, seemingly innocuous function, get_called_class. On its own, it really
doesn't do much. This simple little function, however, plugs a sizeable hole in PHP's object-oriented feature-set and makes it possible to write some really elegant code.
Just one of the things it allows us to do is build a complete, all singing, all dancing Singleton base-class that we can simply
extend to create any number of
fully functioning singletons.
Roughing-Out the ClassWe can start by creating an abstract class – there won't ever be a need to get an instance of the Singleton class – with stubs for the main methods.
01 <?php
02
03 abstract class Singleton {
04
05 protected function __construct() {
06 }
07
08 final public static function getInstance() {
09 }
10
11 final private function __clone() {
12 }
13 }
The first thing we've done is create a protected constructor: although we don't want our
users creating instances directly, we
do want subclasses of Singleton to be able to implement their own initialization code – so we can't make it private.
Next, we've got a public, static method, “getInstance”, that'll serve-up the single instance of a subclass. (“getInstance”, or simply “instance”, appear to be the preferred names for the instance getter in implementations of the singleton pattern, at least in PHP.) The method
must be at class-level (i.e. static) because we'll be needing privileged access to the class' internals now that we've hidden the constructor from the outside world. getInstance is additionally marked “final” to prevent subclasses re-implementing it. (If there
was a need to re-implement getInstance, which is small and well focused, it
may be a sign that the subclass isn't actually a Singleton after all, or it may be that we need to expand our definition of the singleton pattern.)
Finally, we've created an empty, private implementation of the magic “__clone” to prevent
anyone cloning an instance – using the keyword “clone”. If it were possible to clone an instance, the class could no longer claim to be a singleton and the integrity of our code could be in jeopardy. __clone is marked “final” only for completeness: since it is already private, subclasses won't be able to re-implement it anyway.
SolutionNow, let's take a look inside getInstance, the real meat of the pattern.
01 <?php
02
03 abstract class Singleton {
04
05 protected function __construct() {
06 }
07
08 final public static function getInstance() {
09 static $aoInstance = array();
10
11 $calledClassName = get_called_class();
12
13 if (! isset ($aoInstance[$calledClassName])) {
14 $aoInstance[$calledClassName] = new $calledClassName();
15 }
16
17 return $aoInstance[$calledClassName];
18 }
19
20 final private function __clone() {
21 }
22 }
The first thing you'll notice is an
array of instances. If you remember, our objective is to create a class that we can just
extend to create a working singleton: we don't want to have to implement
any singleton-related code further down the class hierarchy. Since we want to inherit
all functionality in subclasses, the getInstance method must store
many single instances: one for each subclass of Singleton. Once we get round to actually storing instances, we'll key each of them on the name of the class to which they belong.
Now, to create and store an instance. Using PHP's extremely useful variable variables we can instantiate a class by name. The problem is we must first get hold of the name of the class to instantiate. Let's say we're gearing-up to create a DatabaseConnection subclass – the “Hello World!” of singleton examples. Once written, we want to be able to call DatabaseConnection::getInstance() to fetch the instance. We get the name of the class we're calling, the name in front of the two colons - “DatabaseConnection” in the case of the previous example - using the new function, get_called_class.
Testing the ClassAll that remains is to introduce some tests to prove that Singleton does actually do what it says on the tin. Here's a script, written for SimpleTest, that tests two key aspects of our class.
01 <?php
02
03 //@todo Load SimpleTest
04
05 //@todo Load Singleton
06
07 class TestSingleton01 extends Singleton {
08
09 protected function __construct() {
10 }
11 }
12
13 class TestSingleton02 extends Singleton {
14
15 protected function __construct() {
16 }
17 }
18
19 class TestSingleton03 extends TestSingleton01 {
20
21 protected function __construct() {
22 }
23 }
24
25 class SingletonUnitTestCase extends UnitTestCase {
26
27 public function testGetInstanceAlwaysReturnsSingleInstanceOfSubclass() {
28 $this->assertTrue (
29 TestSingleton01::getInstance() instanceof TestSingleton01
30 );
31
32 $this->assertReference (
33 TestSingleton01::getInstance(),
34 TestSingleton01::getInstance()
35 );
36 }
37
38 public function testGetInstanceReturnsCorrectInstance() {
39 $this->assertTrue (
40 TestSingleton01::getInstance() instanceof TestSingleton01
41 );
42
43 $this->assertReference (
44 TestSingleton01::getInstance(),
45 TestSingleton01::getInstance()
46 );
47
48 $this->assertTrue (
49 TestSingleton02::getInstance() instanceof TestSingleton02
50 );
51
52 $this->assertReference (
53 TestSingleton02::getInstance(),
54 TestSingleton02::getInstance()
55 );
56
57 $this->assertTrue (
58 TestSingleton03::getInstance() instanceof TestSingleton03
59 );
60
61 $this->assertReference (
62 TestSingleton03::getInstance(),
63 TestSingleton03::getInstance()
64 );
65 }
66 }
The first test checks if getInstance always returns the same instance when called repeatedly against a subclass. The second test is almost certainly over the top, but ensures that no matter what, Singleton will always return the
correct instance.