Singleton-aware Factory in PHP

I’m using a Factory (pattern) to load objects based on configuration. Explaining the reasoning is beyond the scope of this post, the link to Wikipedia might help here.
Here’s some code how it was:

$instances[$class_name] = new $class_name();
...
return $instances;

Pretty simple, really. The $class_name came from the configuration, based on the requested type of object. It could also create an array of objects, if configured.
But now I wanted to add a singleton class this way, I wanted it to be configurable, and loadable via the Factory, but I didn’t want to have multiple instances of that class.

I had to try a few things, as there’s a bug in PHP (that they classified as “Won’t fix”), so I had to use the workaround with the PHP function call_user_func, instead of using is_callable.
First the code that doesn’t work. I think it would’ve been nicer, but well, its not an option:

if (method_exists($class_name, '__construct') and
   (! in_array('__construct', get_class_methods($class_name))))
{
  is_callable(array($class_name, 'get_instance'), true, $callable_name);
  $instances[$class_name] = $callable_name();
}
else
{
  $instances[$class_name] = new $class_name();
}

So the condition checks if the constructor exists in the class (method_exists), and it will return true even if the constructor is private or protected. Then it also checks if the constructor is returned by get_class_methods, which does not return private and protected methods!
Always remember, a singleton should have a private or protected constructor, to prevent multiple instances. Therefor this should always work, and it does, for me.
The is_callable sets the passed reference $callable_name to the callable class/method string, but if called, it threw an error and said the function was not found (e.g. “classname::get_instance”), even though it definitely was there and worked if called directly.

So, the working solution is this:

if (method_exists($class_name, '__construct') and
   (! in_array('__construct', get_class_methods($class_name))))
{
  $instances[$class_name] = call_user_func(array($class_name, 'get_instance'));
}
else
{
  $instances[$class_name] = new $class_name();
}

Using the call_user_func function, with the classname and the static method name (“get_instance”, which I use throughout all code for singletons) works just as expected!

So now the Factory can instantiate or just return singleton class objects as well as “normal” class objects.

The class I used extended an abstract class which had the singleton methods defined. Maybe this caused the problem? But there shouldn’t be any difference, so I guess this is the best solution here.
True thing is, the Factory is not really singleton aware, but works transparently with singletons. Ah well…

Advertisement
Singleton-aware Factory in PHP

3 thoughts on “Singleton-aware Factory in PHP

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s