Today it was one of my most productive days. After a JCE plugin for K2 content items and putting modules inside tabs, I decided to do some PHP hacking, with great results. The object of my pursuit was to create a variation of the Factory pattern, written in PHP5, which can be serialized and unserialized at will. Purists will observe that my implementation is not a direct implementation of the Factory design pattern. In fact, it is modelled as a serializable version of the Joomla! 1.5 JFactory class, which provides static methods for instanciating Singletons. Let's dive to the code, OK?
First things first. Here are our design goals:
- The Factory will only instanciate Singletons, using static (class) methods.
- The Factory doesn't allow to be instanciated as an object, thus avoiding misuse.
- We must be able to serialize it and unserialize it at will. The serialization should take care to serialize the objects our Factory has instanciated. This enables us to save the state of a part - or the whole! - of an application to persistent storage (e.g. database, file) and recall it in a later page call. This is useful for PHP applications doing a lot of work which has to be spanned accross multiple page loads, e.g. backing up a site or running a long data mining algorithm.
- Creating new static methods which load other Singleton classes should be as short as possible.
Let's see at the implementation. First, meet our Factory class, named Foobar:
File Foobar.php | |
1 |
<?php |
Damn, that's compact, isn't it? Do note that I use PHP5's __autoload magic function to avoid having to use require()'s throughout my client code. I will let PHP automatically load the missing class files from the same directory as Foobar.php. Saves a lot of headache when unserializing, as you don't have to know which objects were created beforehand so that you have a chance to include() or require() the respective class file. Less headache implementing equals less effort debugging, in a demonstration of the KISS principle.
The secret sauce is two-fold. First, we have the $objectlist private variable. This is an array, holding at most one instance of any class our Factory can instanciate into objects. This allows us to make the getClassA function act as if ClassA is a Singleton (see the implementation of ClassA below to find out why I say "as if"). It also allows us to serialize all instanciated obejcts.
The other part of the secret sauce is that I lied when I said that you can't instanciate this class. Yes, I lied. You can instanciate it, but only if you do it from a method inside the class, since the constructor is marked protected! This effectively means that the static (class) methods delegate their functionality to a static Factory object. So, while the Factory is a class with static functions to the outside world, it acts as a Singleton for its own methods. The serialization is performed against this static object and deserialization instanciates, once more, this static object.
If we want to extend this Factory class to instanciate objects of a different type, let's say ClassB all we have to do is to add a two-liner method declaration:
1 |
static function getClassB() |
Wow! Of course, you can extend this idea with more business logic behind instanciating an object. For example, you could check some app-specific condition and run a variation of the getClassInstance code to instanciate ClassA or ClassB depending on the condition. One practical example, instanciating ClassA on Linux hosts and ClassB on Windows hosts. The only thing which matters is making sure that new objects are inserted in the $objectlist variable and fetched from there if they already exist.
And now, our not-very-exciting pseudo-Singleton class, ClassA:
File ClassA.php | |
1 |
<?php |
Nothing fancy, it's merely able to set and get a value variable. The real magic of this class is it's __construct() constructor. I began by telling you that it's a Singleton class. I lied. A bit. The constructor is simply crafted in such a way as to not allow an object to be instanciated from the class, unless this is done by our Foobar class. Since Foobar stores ClassA's one and unique instance in the $objectlist array, it effectively behaves as a Singleton.
Let's see how this can be used. We'll start with the first page, which creates an object of ClassA, sets its value to the answer to the Ultimate Question of Life, the Universe and Everything and serializes the Factory to a file named "data":
File page1.php | |
1 |
<?php |
Next up, let's create a page which load the serialized Factory, gets an object of ClassA and displays the stored value. Mind you, this is a separate PHP file, run after page1.php has finished and exited. Our data was, in the meantime, in a serialized state (I jokingly call it a state of "suspended animation") inside the "data" file. This data could be stored for an indefinite period of time - we do need the Earth to finish calculating the Ultimate Question of Life, the Universe and Everything before we recall the answer, right? LoL!
1 |
<?php |
As you can see this little page correctly displayed the stored value: 42.
If you are seeking practical examples of this code, you'll have to wait a bit. I am redesigning the JoomlaPack backup engine to fully utilize PHP5's features. This is the first change I made, so you can say that this pattern can be used in site backup applications. This could also be used in interactive web application where the user manipulates complicated data in multiple steps, e.g. an interactive PHP-based finite elements simulation, even a shopping cart, etc.