Implementing a Singleton design pattern for PHP 5.4
It would seem, of all the design patterns, what could be simpler than the well-known singleton. In many classical examples of implementation in different programming languages, it can take only a couple of dozen lines, and even less.
It so happened that I have been implementing this template for the second year since the first release of PHP 5.3 in 2009. At that time, its predecessor, version 5.2, did not have late static binding, and in order to create an instance of a class, it was necessary to transfer its name to the method, which seemed archine-friendly to me.
With the release of PHP 5.4, having looked once again at the old implementation and at the new language features, I rewrote this pattern once again by getting - as it seemed to me then and it seems now - the final version. ')
Implementation details below. I would like to immediately note the main features:
Parametric generation . Allows you to create instances of classes using the method call of the :: getInstance method. Each signature will have its own class instance. By default, this type of spawning is disabled. Included in child classes by overriding the :: useParametricInstantiation method.
Getting a child by its parent class name . Allows you to refer to child classes from the parent as well as from other classes without knowing their name.
Creating a child class by the name of the parent class . Similar to the second paragraph, only if the child was not * Trait TSingleton. * An implementation of the Singleton design pattern. * / namespace Traits; / ** * var array variable holding all created objects. * / $ objectPool = [];
trait TSingleton {
/ ** * Do not allow to create an object. * * final * access private * return void * / function __construct () {}
/ ** * Do not allow cloning object. * * final * access private * return void * / private function __clone () {}
/ ** * Called when class is being instantiated. * * access protected * return void * / protected function onCreate () {}
/ ** * Returns true if child class has been specified by the mask. * * param string $ child * param string $ parentMask * final * static * access public * return boolean * / static function hasParentClass ($ child, $ parentMask) { $ currentClass = get_parent_class ($ child); if (! $ currentClass) return false; do { if (strpos ($ currentClass, $ parentMask)! == false) return true; } while ($ currentClass = get_parent_class ($ currentClass));
return false; }
/ ** * Returns instance of child class using its parent 'class name specified * by the mask. Always returns an array. * * param string $ parentMask Any substring of parent's fully qualified class name. * final * static * access public * return array | null * / static function getObjectByParent ($ parentMask) { global $ objectPool;
foreach ($ objectPool as $ class => $ container) if (self :: hasParentClass ($ class, $ parentMask)) return array_values ($ container);
return null; }
/ ** * Finds object (s) of their (their) parent's class name. If not * found the method will create it. Always returns an array. * * param string $ parentMask * param array $ initArgs * final * static * access public * return array | null * / static function getObjectByParentSafe ($ parentMask, $ initArgs = []) { $ child = self :: getObjectByParent ($ parentMask); if ($ child! == null) return $ child;
// Look up all declared classes. $ result = []; foreach (get_declared_classes () as $ class) { if (self :: hasParentClass ($ class, $ parentMask)) { $ result [] = call_user_func_array (($ class. ':: getInstance'), $ initArgs); } }
return count ($ result)? $ result: null; }
/ ** * Returns child object (s) of the parent class. * * see TSingleton :: getObjectByParent * final * static * access public * return array | null * / static function getMyChild () { return self :: getObjectByParent (get_called_class ()); }
/ ** * Safe variant of :: getMyChild. * * see TSingleton :: getObjectByParentSafe * final * static * access public * return array * / static function getMyChildSafe () { $ initArgs = func_get_args (); return self :: getObjectByParent (get_called_class (), $ initArgs); }
/ ** * Returns class instance. * * static * final * access public * return TSingleton * / static function getInstance () { global $ objectPool;
$ argsArray = func_get_args (); $ class = get_called_class ();
if (static :: useParametricInstantiation () && count ($ argsArray)) { $ fingerprint = "; foreach ($ argsArray as $ arg) $ fingerprint. = serialize ($ arg); $ key = md5 ($ class. $ fingerprint); } else // Use class name as a key. $ key = $ class;
if (! isset ($ objectPool [$ class])) $ objectPool [$ class] = []; // Init class objects container.
/ ** * Enables or disables the parametric class instantiation. Disabled by default. * * access public * static * return boolean * / static function useParametricInstantiation () { return false; } } : All created objects are now stored in one variable. The scope of this variable (at the request of the working masses) is limited by the admixture.
In order not to tear the templates with a non-standard template, a singleton with parametric generation is now called a multiton (Multiton).