Creating PHP Singleton Classes

Singleton Class saves resources

There are several ways to implement singleton classes.
In all methods, the class instance is created as

\Namespace\ClassName::getInstance()

Method 1:

 protected static $instances =  array();// to solve  the issue https://stackoverflow.com/questions/17632848/php-sub-class-static-inheritance-children-share-static-variables
    /**
     *  @brief Singleton Constructor
     *  
     *  @return ClassInstance
     *  
     *  @details Caution: never call Class::getInstance() in another class's constructor, that instance will be discarded from $instances array
     */
    public static function getInstance()// Caution: never call Class::getInstance() in another class's constructor
    {        
        //https://www.php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php &
        //https://refactoring.guru/design-patterns/singleton/php/example
        $ref  = new \ReflectionClass( get_called_class() ) ;         
        $reflectionProperty = new \ReflectionProperty(static::class, 'instances');
        $reflectionProperty->setAccessible(true);
        //echo $reflectionProperty->getValue();
        $instances =   $reflectionProperty->getValue();;//$reflectedClass->getStaticPropertyValue('inst');
        $intentedClass = static::class;
        if (  !isset($instances[$intentedClass]))
        {
            // The magic.
            //$ctor->setAccessible( true ) ;
            //$inst = new static();
            $instances[$intentedClass] = new static();
            //echo "INSTANTIATED ".print_r($inst,true) ."<br />";

            $reflectionProperty->setValue(null/* null for static var */, $instances);
            //echo "<pre>". print_r(array_keys($instances),true)."</pre>";
        }       
        return $instances[$intentedClass] ;
    }//public static function getInstance()

Method 2: With 'Constructor Overloading'

protected static $instances =  array();// to solve  the issue https://stackoverflow.com/questions/17632848/php-sub-class-static-inheritance-children-share-static-variables
    public static function getInstance()
    {
        //https://www.php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php &
        //https://refactoring.guru/design-patterns/singleton/php/example
        $ref  = new \ReflectionClass( get_called_class() ) ;
        $reflectionProperty = new \ReflectionProperty(static::class, 'instances');
        $reflectionProperty->setAccessible(true);
        //echo $reflectionProperty->getValue();
        $instances =   $reflectionProperty->getValue();;//$reflectedClass->getStaticPropertyValue('inst');
        $intentedClass = static::class;
        //if (  $instances[static::class] == null)
        if (  !isset($instances[$intentedClass]))
        {
            $arguments = func_get_args();
            $numberOfArguments = func_num_args();
            //die("numberOfArguments  is " . $numberOfArguments );
            // The magic.
            //$ctor->setAccessible( true ) ;
            //$inst = new static();
            $instances[$intentedClass] = $numberOfArguments==0?new static():new static($arguments[0] /* $dbDetails */);
            //echo "INSTANTIATED ".print_r($inst,true) ."<br />";            
            $reflectionProperty->setValue(null/* null for static var */, $instances);
            //echo "<pre>". print_r(array_keys($instances),true)."</pre>";        
        }
        return $instances[$intentedClass] ;
    }//public static function getInstance($dbDetails)
    private function __construct()
    {
        $arguments = func_get_args();
        $numberOfArguments = func_num_args();
        //die("numberOfArguments  is " . $numberOfArguments );
        //die("calling __construct in  \MYSQL");
        if (method_exists($this, $function = '__construct'.$numberOfArguments)) {
            call_user_func_array(array($this, $function), $arguments);
        }
    }
    /**
     *  @brief Constructor without argument
     *  
     *  @return void
     *  
     *  @details This construstor is used when this class is used as a library, where $dbDetails can be obtained from  a config class of that system
     */
    //--------------------------------------------------------
    private function __construct0()//MySQL(
    //--------------------------------------------------------
    {
        $siteConfig =  \OsolMVC\Core\Config\ClassSiteConfig::getInstance();
        $dbDetails = $siteConfig->getDBSettings();
        //die("dbDetails is <pre>".print_r($dbDetails,true)."</pre>");

        //die("calling __construct0 in  \MYSQL");

        $this->dbDetails = $dbDetails;
        $this->user = $dbDetails['DB_USER'];
        $this->pass = $dbDetails['DB_PASS'];
        $this->server = $dbDetails['DB_SERVER'];
        $this->db = $dbDetails['DB_NAME'];
        $this->table_prefix = $dbDetails['table_prefix'];
        $this->log_queries = $dbDetails['log_queries'];
        $this->query_log_type = $dbDetails['query_log_type'];
        $this->connectdb();

    }//private function __construct()
    //--------------------------------------------------------
    private function __construct1($dbDetails)//MySQL(
    //--------------------------------------------------------
    {
        //die("calling __construct1 in  \MYSQL");

        $this->user = $dbDetails['DB_USER'];
        $this->pass = $dbDetails['DB_PASS'];
        $this->server = $dbDetails['DB_SERVER'];
        $this->db = $dbDetails['DB_NAME'];
        $this->table_prefix = $dbDetails['table_prefix'];
        $this->log_queries = $dbDetails['log_queries'];
        $this->query_log_type = $dbDetails['query_log_type'];
        $this->connectdb();
    }

Method 3. Implement it in Parent Class

Note:

  1. Child class constructors should be protected and NOT private
    ie

    protected function __construct()
  2. While using this approach, Dont instantiate a singleton class inside constructor of another singleton class. when called inside constructor of another singleton class, the instance, instantiated inside constructor, is discarded after the former is instantiated.

Full Code of parent class

class CoreParent
{
    protected static $instances =  array();// to solve  the issue https://stackoverflow.com/questions/17632848/php-sub-class-static-inheritance-children-share-static-variables
    /**
     *  @brief Singleton Constructor
     *  
     *  @return ClassInstance
     *  
     *  @details Caution: never call Class::getInstance() in another class's constructor, that instance will be discarded from $instances array 
     */
    public static function getInstance()// Caution: never call Class::getInstance() in another class's constructor
    {

        //https://www.php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php &
        //https://refactoring.guru/design-patterns/singleton/php/example
        $ref  = new \ReflectionClass( get_called_class() ) ;

        $reflectionProperty = new \ReflectionProperty(static::class, 'instances');
        $reflectionProperty->setAccessible(true);
        //echo $reflectionProperty->getValue();
        $instances =   $reflectionProperty->getValue();;//$reflectedClass->getStaticPropertyValue('inst');
        $intentedClass = static::class;
        //if (  $instances[static::class] == null)
        if (  !isset($instances[$intentedClass]))
        {

            // The magic.
            //$ctor->setAccessible( true ) ;
            //$inst = new static();
            $instances[$intentedClass] = new static();
            //echo "INSTANTIATED ".print_r($inst,true) ."<br />";

            $reflectionProperty->setValue(null/* null for static var */, $instances);
            //echo "<pre>". print_r(array_keys($instances),true)."</pre>";
        }

        return $instances[$intentedClass] ;
    }//public static function getInstance()

}//class CoreParent