Namek Dev
a developer's log
NamekDev

Common code for Singletons using PHP's traits

February 22, 2015

I hate singletons. Some say that singletons are not bad, instead “singletonism” is the evil. I would argue. Anyway, I’ve been working on some common Singleton pattern code in PHP for a particular reason - I had to create common code for two basecodes:

  1. Laravel app using Facades
  2. some other custom API

Usage

class DataUtilsInst {
	use Singleton;

	public static function getClassName() {
		return '\NS\common\utils\DataUtils';
	}
}

Singleton code

trait Singleton {
	static $__INSTANCES = [];

	public static function getInstance() {
		$className = static::getClassName();

		if (!isset(static::$__INSTANCES)) {
			static::$__INSTANCES = [];
		}

		if (!isset(self::$__INSTANCES[$className])) {
			self::$__INSTANCES[$className] = new $className;
		}

		return self::$__INSTANCES[$className];
	}

	/**
	 * Handle dynamic, static calls to the object.
	 *
	 * @param  string  $method
	 * @param  array   $args
	 * @return mixed
	 */
	public static function __callStatic($method, $args)
	{
		$instance = static::getInstance();

		switch (count($args))
		{
			case 0:
				return $instance->$method();

			case 1:
				return $instance->$method($args[0]);

			case 2:
				return $instance->$method($args[0], $args[1]);

			case 3:
				return $instance->$method($args[0], $args[1], $args[2]);

			case 4:
				return $instance->$method($args[0], $args[1], $args[2], 

$args[3]);

			default:
				return call_user_func_array(array($instance, $method), 

$args);
		}
	}
}

I took inspiration from Laravel:

  1.  __callStatic comes from Facade
  2. instancing came from Illuminate\Container’s share() method often used in service providers.

static function getClassName???

Yeah, you can change trait to class which will be extended and use get_called_class() instead but here’s why it didn’t work for me:

  1. It’s impossible to extend class by multiple classes in PHP, so I used trait. It’s rather future benefit.
  2. I didn’t want to make long calls to Singleton like DataUtils::getInstance()->getFromCache($key, ..). Instead,  DataUtilsInst::getFromCache($key, …) works better for me and actually makes it quite impossible (without dirty hacks) to save reference.

Wait, why would I need getInstance()? Because __callStatic() is called when called static function name isn’t used by any class method. Speaking otherwise, when you call someImportantFunction() wanting to get called it through __****callStatic(), then PHP throws error stating that tou can’t run a non-static function statically!

Non-static method MyUtils::someImportantFunction() should not be called statically, assuming $this from incompatible context

More on this issue can be found on PHP (not a) bug report.

php
comments powered by Disqus