Using a Singleton to Manage Your Singletons. Wait, What?

I use the Singleton Design Pattern quite a bit, so much in fact, that I needed something to make using Singletons more seamless!

If you're not familiar with the Singleton Design Pattern, or how it pertains to PHP, then I suggest you check it out here.

If you are familiar with the pattern, and like me, have grown tired of adding getInstance() to all your classes and privatizing their constructors, then say hello to the Singleton class, known among his inner circle, as Mr. Singleton.

What's Wrong with the Standard Singleton Implementation?

Nothing whatsoever! But in order to convert your class to use the Singleton pattern, you have to do just that: convert all your classes. That's great if the only implementation you ever intend to use utilizes the getInstance() method. This may be exactly what you want for your database abstraction layer, or your helper classes that aren't static, or one of a gazillion other Singleton-worthy classes.

If you're just trying to cut some overhead by ensuring that your classes only get instantiated once per execution, then the Singleton contract might not be appropriate. Additionally, if you didn't have to refactor all your classes and bind them to the pattern, why would you?

Enter Mr. Singleton.

What Mr. Singleton Does for You

Basically, Mr. Singleton is just a final class that can't be constructed, that sits around waiting for you to request a class. When you do, he assumes the role of elephant, remembering every class you ask him for. If you ask him for it again, he gives you the original instance back, thereby preventing the overhead of additional instances.

Since Mr. Singleton creates the actual object, and remembers its state, all you have to do is ask him for the object, run the methods, and move along. You get all the benefits of the Singleton pattern without the disadvantages of refactoring your code. You can reduce overhead, and leave any preexisting or future code intact!

But enough of the cheesy code personification, let's have an example, shall we?

The Singleton Class

The code is pretty simple, just a few static methods and a member array to hold the instances.

toggle plain-text
  1. <?php
  2.  
  3. final class Singleton
  4. {
  5.     /**
  6.      * Maintains collection of instantiated classes
  7.      */
  8.     private static $instances = array();
  9.    
  10.     /**
  11.      * Overload constructor
  12.      */
  13.     private function __construct(){}
  14.    
  15.     /**
  16.      * Manages instantiation of classes
  17.      *
  18.      * @param $class
  19.      *
  20.      * @return none
  21.      */
  22.     public static function instance($class)
  23.     {      
  24.         //instantiate class as necessary
  25.         self::create($class);  
  26.        
  27.         //return instance
  28.         return self::$instances[$class];
  29.     }
  30.    
  31.     /**
  32.      * Creates the instances
  33.      *
  34.      * @param $class
  35.      *
  36.      * @return none
  37.      */
  38.     private static function create($class)
  39.     {
  40.         //check if an instance of requested class exists
  41.         if (!array_key_exists($class , self::$instances))
  42.         {
  43.             self::$instances[$class] = new $class;
  44.         }
  45.     }
  46. }

Let's test Mr. Singleton with a sample class and some calls.

You can place the sample code in its own file and include Mr. Singleton, or you can just append it to the same file.

toggle plain-text
  1. <?php
  2. /**
  3.  * Test Taco Class
  4.  */
  5. class Taco
  6. {
  7.     private $shell = 'soft flour';
  8.    
  9.     public function __construct()
  10.     {
  11.         print "constructed\n";
  12.     }
  13.    
  14.     public function shell()
  15.     {
  16.         return $this->shell . "\n";
  17.     }
  18.    
  19.     public function change_shell()
  20.     {
  21.         $this->shell = 'hard corn';
  22.     }
  23. }
  24.  
  25. // Ask Mr. Singleton for a taco.
  26. $taco = Singleton::instance('Taco');
  27. print $taco->shell();
  28.  
  29. // We'd like to have another taco.
  30. $taco2 = Singleton::instance('Taco');
  31.  
  32. // Ah, it's the same taco!
  33. print $taco2->shell();
  34.  
  35. // Change the taco a bit
  36. $taco2->change_shell();
  37. print $taco->shell();
  38.  
  39. // Can we we have another taco?
  40. $taco3 = Singleton::instance('Taco');
  41.  
  42. // Dang, it's the same taco.
  43. print $taco3->shell();

Summary

I utilize Mr. Singleton in a lot of my projects, and have grown quite fond of the ability to use him for quick one-off method calls without having to create a new instance of the class I am calling, even if I'm not concerned about redundant instances. Since Mr. Singleton's instance() method returns the instance of the requested object, as shown above in the samples, you can easily chain the first method call right to the back of your request!

Extend and Improve

As always, this could be extended. A lot of Singleton implementations tend to overload additional magic methods, especially __clone(). My goal here is less of a code-by-contract, and more of an ease-of-use approach. If preventing the clone of your objects is important to your project, then by all means, overload the __clone() method.

};