📜 ⬆️ ⬇️

Singleton and Late static binding

The number of singletons in a project is often directly proportional to its complexity and size. Naturally, to describe a private constructor, a static property-object and a method for obtaining it for any appreciable number of classes is a bit tedious, and perhaps not true. This raises the question: how to “put out of the brackets” the implementation of Singleton?

The options here in my opinion may be several. The first is to revise the architecture of the application. It may be easier to register all objects applying for Singleton in a certain collection and access them exclusively through it. This will avoid accidentally creating two copies of the object. However, it is safe to say that there is only one copy of an object in an application at a certain point in time, in this situation it is impossible, since no one can prevent the creation of an object directly, bypassing the collection.

The second way is to implement the Singleton superclass, from which all Singleton classes will be inherited. However, in its implementation in PHP there is one pitfall - late binding (late / dynamic binding) in static methods. More precisely - the definition of the name of the current class in the static getInstance method.
Standard tools like the get_class method cannot be applied here, since we still don’t actually have any object. A reflection comes to mind, but you quickly understand that it has nothing to do with it, since the starting point for it is the name of the object, which we are actually trying to find out.
And here we come to the aid of a mechanism called "late static binding" (late static binding) , available starting with version 5.3.0. Its essence lies in the ability to refer to the called class in the context of static inheritance. In other words, starting from version 5.3.0, we can use the get_called_class function to get the name of the class being called within the static method.

Knowing the above, it is easy to create a superclass to implement the Singleton pattern.
')
/** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  1. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  2. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  3. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  4. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  5. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  6. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  7. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  8. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  9. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  10. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  11. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  12. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  13. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  14. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  15. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  16. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  17. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  18. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  19. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  20. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  21. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  22. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  23. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  24. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  25. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  26. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  27. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  28. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  29. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
  30. /** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .
/** * Singleton pattern implementation */ abstract class Singleton { /** * Collection of instances * @var array */ private static $_aInstance = array(); /** * Private constructor */ private function __construct(){} /** * Get instance of class */ public static function getInstance() { // Get name of current class $sClassName = get_called_class(); // Create new instance if necessary if ( !isset( self::$_aInstance[ $sClassName ] ) ) self::$_aInstance[ $sClassName ] = new $sClassName(); $oInstance = self::$_aInstance[ $sClassName ]; return $oInstance; } /** * Private final clone method */ final private function __clone(){} } * This source code was highlighted with Source Code Highlighter .


Let's add the code with a small test

  1. class Example extends Singleton {}
  2. $ oExample1 = Example :: getInstance ();
  3. $ oExample2 = Example :: getInstance ();
  4. echo (is_a ($ oExample1, 'Example' ) && $ oExample1 === $ oExample2)
  5. ? 'Same' : 'Different' , "\ n" ;
* This source code was highlighted with Source Code Highlighter .


the result of which will be, you guessed it, “Same”.

It is worth noting that there are mechanisms for obtaining the name of the called class in the context of a static function in PHP version 5.3.0 using the analysis of the call stack using the debug_backtrace method. However, in my opinion, this approach is rectal a little more than full.

PS
I agree that inheritance is not the best mechanism in this case. In my opinion, if PHP supported annotations and AOP, it would be much more convenient to mark the classes with the Singleton annotation and implement the aspect that injects the code necessary for the implementation of Singleton to the marked classes. However, as far as I know, neither annotations (as language constructs), nor AOP in PHP are planned in the near future.

Source: https://habr.com/ru/post/85852/


All Articles