2010.02.24

PHP < 5.3 – Lazy Singleton Class

I’ve been doing some PHP lately (and liking a lot) and one of the cool features of PHP5 comparing to Actionscript3 is that you can have private constructors, which means that it’s really easy to create “real static classes” and singletons without hacks!

Before starting it, I have to say that I DO NOT RECOMMEND using the LazySingleton Class described on this post, - changed my mind, read the comments to know why added 2010-07-16 - it’s more a proof of concept than a real usage Class… having said that, here is an example of a regular PHP5 singleton class:

/**
 * Based on PHP documentation example
 * (http://php.net/manual/en/language.oop5.patterns.php#language.oop5.patterns.singleton)
 */
class SingletonExample{

	// Store class instance
	private static $instance;
	
	// A private constructor; prevents direct creation of object
	private function __construct(){
		echo 'I am constructed';
	}

	// Get Singleton Instance
	public static function getInstance(){
		if (!isset(self::$instance)) {
			$c = __CLASS__; //get class name
			self::$instance = new $c; //create new instance
		}
		return self::$instance;
	}
	
	// Example method
	public function bark(){
		echo 'Woof!';
	}

	// Prevent users to clone the instance
	private function __clone(){
		trigger_error('Clone is not allowed.', E_USER_ERROR);
	}
}

The problem is that some times you may need to create many different Singleton Classes and copying the getInstance() method on all classes doesn’t feel right (because you’re repeating yourself).

The first thing that I tried was to find a way to create an Abstract Singleton Class and extend it, but I think it is only possible with PHP 5.3 (#1, #2). So I decided to use a different approach and use “a design pattern that I don’t know the name” and create a static class that would do the whole checking, instantiation and storage of the instances of all the Singleton Classes of my application. My approach is similar to what Francis Turmel did on the LazySingleton class of his AS3 framework (that’s why I used the same name). Here is my implementation:

/**
 * Singleton Factory - based on Francis Turmel (http://www.nectere.ca) AS3 implementation
 * @author Miller Medeiros (http://www.millermedeiros.com)
 * @version 1.0 (2009/12/17)
 */
class LazySingleton {
	
	private static $references = array();
	private static $allowBuild = array();
	
	/**
	 * Constructor - Static Class
	 * @private
	 */
	private function __construct() {}
	
	/**
	 * Get Object instance.
	 * @param string $class_name Class name (can be retrieved using '__CLASS__' constant).
	 * @return object Instance
	 */
	public static function getInstance($class_name){
		if(!array_key_exists($class_name, self::$references)) {
			self::$allowBuild[$class_name] = TRUE;
			self::$references[$class_name] = new $class_name();
			unset( self::$allowBuild[$class_name] );
		}
		return self::$references[$class_name];
	}
	
	/**
	 * Check if the Class was instantiated using the getInstance() method.
	 * @param string $class_name Class name.
	 */
	public static function validate($class_name){
		if(! isset(self::$allowBuild[$class_name]) || isset(self::$references[$class_name]) ){
			trigger_error("$class_name is a Singleton Class and can only be instantiaded using the getInstance() method.", E_USER_ERROR);
		}
	}
	
}

and here is the Interface:

/**
 * Interface for Singleton classes that uses LazySingleton instantiation.
 * @author Miller Medeiros (htt://www.millermedeiros.com)
 * @version 1.0 (2009/12/17)
 */
interface ILazySingleton {
	/**
	 * Should call LazySingleton::validate(__CLASS__) before starting the Class construction.
	 */
	function __construct();
	/**
	 * Should call LazySingleton::getInstance(__CLASS__)
	 * @return object instance
	 */
	static function getInstance();
	/**
	 * Prevents object clone (should trigger_error) if called or at least don't do anything.
	 */
	function __clone();
}

Here is how to use it:


require_once 'LazySingleton.php';
require_once 'ILazySingleton.php';

class ExampleLazySingleton implements ILazySingleton{
	
	//will throw an error if called directly
	public function __construct(){
		LazySingleton::validate(__CLASS__); //check if class can be instantiated
		// **class initialization should come here**
	}
	
	//shouldn't be called
	public function __clone(){
		trigger_error('Clone is not allowed.', E_USER_ERROR);
	}
	
	//get class instance
	public static function getInstance(){
		return LazySingleton::getInstance(__CLASS__);
	}
	
	// example method
	public function bark(){
		echo 'Woof!';
	}
	
}

// example accesing method
$my_obj = ExampleLazySingleton::getInstance();
$my_obj->bark(); //output 'Woof!'

$my_other_obj = ExampleLazySingleton::getInstance();
$my_other_obj->bark(); //output 'Woof!'

The advantage of using the LazySingleton is that you reduce the complexity of the getInstance() method making it easier to create a new Singleton Class but the drawback is that the performance is worse (since you have to access another Class and store/retrieve the instances from arrays)see comments added 2010-07-16 – and the __construct() method is “exposed” (public).

Since PHP allows you to have private constructors you should just do it on the old way (copy the getInstance method) until PHP 5.3 starts being supported on your clients servers, then I would start extending an Abstract Singleton Class to avoid code duplication.changed my mind, read the comments to know why added 2010-07-16

I hope the concept was useful for someone. I’ve learned a couple of things while doing this and couldn’t find any similar implementation, that’s why I’m sharing it.

Note: don’t use the Singleton pattern unless you really need it, in most cases using an static class (Class with only static methods/members) is a better approach since you don’t need to create a new instance to “use” the Object and on most of the languages it also uses less memory and has a better performance – I’m not sure about how it affect performance on PHP, theoretically static should be faster since you don’t need to instantiate a new Object and store it on the memory… – read comments for more details.
added 2010-03-23


Comments

You mentioned that the performance is worse when using the LazySingleton class in PHP. Do you know if Francis (who you mentioned) has the same performance issues in AS3 or is it not a problem?

The performance should be worse on both cases since accessing properties/methods from another Object/Class is always slower than local members (inheritance also is not good for performance).

It shouldn't be a big deal since usually you don't need to do the Singleton check/validation often on your code (and you probably don't need many Singletons too), but if performance is a huge concern this is something that could definately be stripped down from your code. OOP isn't the best thing for performance, Object instantiation, loops and property lookup usually are slower than plain procedural code, but that don't mean that you should avoid OOP at all.

In most cases maintanability, organization, encapsulation are way more important than performance.

Cheers.

Hi Miller,

I know this is an old post but wanted to ask a question. After doing some research I found out that Francis is a Tech Director at Firstborn. As a Tech Director and well versed in AS3, why does he continue to use the LazySingleton approach knowing that it is not the best approach?

Maybe that is a question for Francis but I thought you might have some insight since you work at Firstborn.

I think he still uses the LazySingleton for the same reason that I usually use when I'm using his AS3 Library and why I had the idea to do the same in PHP... (didn't asked yet) - the code is cleaner and you reduce the complexity/logic from the singleton class, it's faster to type, you get code completion and it's easier to change in the future in case you need, it's more about organization than anything else.

The way that I do Singletons in AS3 is similar to this, as you can see it involves some extra stuff to enforce compiler errors (like the private class), using the LazySingleton you don't need to worry about those things.

It's not because something isn't recommended that you shouldn't use, sometimes it can be the "right" solution for a problem.

Cheers.

Ahh okay that makes sense Miller. I might have to create my own AS3 version and give it a wirl.

One thing that I've forgot to say is that the LazySingleton only affect the performance of the getInstance method, since you probably won't call that method often - I always store a reference to class before using it multiple times - it is not a big deal at all... It will probably only slow things down if you are calling it inside a loop with many iterations (something that you shouldn't do anyways).

Another important thing that happens is that the reference to your singleton class will be stored on the LazySingleton $references array so it won't be automatically garbage collected (unless you remove the reference from the LazySingleton Object). - that also shouldn't be a big deal since Singletons usually needs to be available during the whole code execution - the same happens using the "classic way", since you always have a static reference to the instance.

An important rule to follow is to use it when it makes sense.

I hope it is clear. Cheers.

That makes sense and good thing to know about calling the getInstance method multiple times.

Thanks for the time Miller in thoroughly explaining the pros/cons of the LazySingleton implementation.

Ön minden bizonnyal látni a lelkesedést a munka írsz. A világ azt reméli, még több szenvedélyes írók, mint te, akik nem félnek megmondani, hogy mennyire hisznek. Mindig kövesse a szíved.

Florian Wolters

I've implemented a reusable PHP component some time ago, that both supports Singletons and Multitons (a. k. a. Repository of Singletons) using traits. That component also supports multiple parameters (for the getInstance method) using Reflection. It can be found here. The documentation for the component is available here.

Leave a Comment

Please post only comments that will add value to the content of this page. Please read the about page to understand the objective of this blog and the way I think about it. Thanks.

Comments are parsed as Markdown and you can use basic HTML tags (a, blockquote, cite, code, del, em, strong) but some code may be striped if not escaped (specially PHP and HTML tags that aren't on the list). Line and paragraph breaks automatic. Code blocks should be indented with 4 spaces.