Creating the Singleton design pattern in PHP5

前端 未结 21 1742
猫巷女王i
猫巷女王i 2020-11-22 04:21

How would one create a Singleton class using PHP5 classes?

相关标签:
21条回答
  • 2020-11-22 05:08

    This article covers topic quite extensively: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

    Note the following:

    • The constructor __construct() is declared as protected to prevent creating a new instance outside of the class via the new operator.
    • The magic method __clone() is declared as private to prevent cloning of an instance of the class via the clone operator.
    • The magic method __wakeup() is declared as private to prevent unserializing of an instance of the class via the global function unserialize().
    • A new instance is created via late static binding in the static creation method getInstance() with the keyword static. This allows the subclassing of the class Singleton in the example.
    0 讨论(0)
  • 2020-11-22 05:09
    /**
     * Singleton class
     *
     */
    final class UserFactory
    {
        /**
         * Call this method to get singleton
         *
         * @return UserFactory
         */
        public static function Instance()
        {
            static $inst = null;
            if ($inst === null) {
                $inst = new UserFactory();
            }
            return $inst;
        }
    
        /**
         * Private ctor so nobody else can instantiate it
         *
         */
        private function __construct()
        {
    
        }
    }
    

    To use:

    $fact = UserFactory::Instance();
    $fact2 = UserFactory::Instance();
    

    $fact == $fact2;

    But:

    $fact = new UserFactory()
    

    Throws an error.

    See http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static to understand static variable scopes and why setting static $inst = null; works.

    0 讨论(0)
  • 2020-11-22 05:09

    You don't really need to use Singleton pattern because it's considered to be an antipattern. Basically there is a lot of reasons to not to implement this pattern at all. Read this to start with: Best practice on PHP singleton classes.

    If after all you still think you need to use Singleton pattern then we could write a class that will allow us to get Singleton functionality by extending our SingletonClassVendor abstract class.

    This is what I came with to solve this problem.

    <?php
    namespace wl;
    
    
    /**
     * @author DevWL
     * @dosc allows only one instance for each extending class.
     * it acts a litle bit as registry from the SingletonClassVendor abstract class point of view
     * but it provides a valid singleton behaviour for its children classes
     * Be aware, the singleton pattern is consider to be an anti-pattern
     * mostly because it can be hard to debug and it comes with some limitations.
     * In most cases you do not need to use singleton pattern
     * so take a longer moment to think about it before you use it.
     */
    abstract class SingletonClassVendor
    {
        /**
         *  holds an single instance of the child class
         *
         *  @var array of objects
         */
        protected static $instance = [];
    
        /**
         *  @desc provides a single slot to hold an instance interchanble between all child classes.
         *  @return object
         */
        public static final function getInstance(){
            $class = get_called_class(); // or get_class(new static());
            if(!isset(self::$instance[$class]) || !self::$instance[$class] instanceof $class){
                self::$instance[$class] = new static(); // create and instance of child class which extends Singleton super class
                echo "new ". $class . PHP_EOL; // remove this line after testing
                return  self::$instance[$class]; // remove this line after testing
            }
            echo "old ". $class . PHP_EOL; // remove this line after testing
            return static::$instance[$class];
        }
    
        /**
         * Make constructor abstract to force protected implementation of the __constructor() method, so that nobody can call directly "new Class()".
         */
        abstract protected function __construct();
    
        /**
         * Make clone magic method private, so nobody can clone instance.
         */
        private function __clone() {}
    
        /**
         * Make sleep magic method private, so nobody can serialize instance.
         */
        private function __sleep() {}
    
        /**
         * Make wakeup magic method private, so nobody can unserialize instance.
         */
        private function __wakeup() {}
    
    }
    

    Use example:

    /**
     * EXAMPLE
     */
    
    /**
     *  @example 1 - Database class by extending SingletonClassVendor abstract class becomes fully functional singleton
     *  __constructor must be set to protected becaouse: 
     *   1 to allow instansiation from parent class 
     *   2 to prevent direct instanciation of object with "new" keword.
     *   3 to meet requierments of SingletonClassVendor abstract class
     */
    class Database extends SingletonClassVendor
    {
        public $type = "SomeClass";
        protected function __construct(){
            echo "DDDDDDDDD". PHP_EOL; // remove this line after testing
        }
    }
    
    
    /**
     *  @example 2 - Config ...
     */
    class Config extends SingletonClassVendor
    {
        public $name = "Config";
        protected function __construct(){
            echo "CCCCCCCCCC" . PHP_EOL; // remove this line after testing
        }
    }
    

    Just to prove that it works as expected:

    /**
     *  TESTING
     */
    $bd1 = Database::getInstance(); // new
    $bd2 = Database::getInstance(); // old
    $bd3 = Config::getInstance(); // new
    $bd4 = Config::getInstance(); // old
    $bd5 = Config::getInstance(); // old
    $bd6 = Database::getInstance(); // old
    $bd7 = Database::getInstance(); // old
    $bd8 = Config::getInstance(); // old
    
    echo PHP_EOL."COMPARE ALL DATABASE INSTANCES".PHP_EOL;
    var_dump($bd1);
    echo '$bd1 === $bd2' . ($bd1 === $bd2)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
    echo '$bd2 === $bd6' . ($bd2 === $bd6)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
    echo '$bd6 === $bd7' . ($bd6 === $bd7)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
    
    echo PHP_EOL;
    
    echo PHP_EOL."COMPARE ALL CONFIG INSTANCES". PHP_EOL;
    var_dump($bd3);
    echo '$bd3 === $bd4' . ($bd3 === $bd4)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
    echo '$bd4 === $bd5' . ($bd4 === $bd5)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
    echo '$bd5 === $bd8' . ($bd5 === $bd8)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
    
    0 讨论(0)
  • 2020-11-22 05:09

    I have written long back thought to share here

    class SingletonDesignPattern {
    
        //just for demo there will be only one instance
        private static $instanceCount =0;
    
        //create the private instance variable
        private static $myInstance=null;
    
        //make constructor private so no one create object using new Keyword
        private function  __construct(){}
    
        //no one clone the object
        private function  __clone(){}
    
        //avoid serialazation
        public function __wakeup(){}
    
        //ony one way to create  object
        public static  function  getInstance(){
    
            if(self::$myInstance==null){
                self::$myInstance=new SingletonDesignPattern();
                self::$instanceCount++;
            }
            return self::$myInstance;
        }
    
        public static function getInstanceCount(){
            return self::$instanceCount;
        }
    
    }
    
    //now lets play with singleton design pattern
    
    $instance = SingletonDesignPattern::getInstance();
    $instance = SingletonDesignPattern::getInstance();
    $instance = SingletonDesignPattern::getInstance();
    $instance = SingletonDesignPattern::getInstance();
    
    echo "number of instances: ".SingletonDesignPattern::getInstanceCount();
    
    0 讨论(0)
  • 2020-11-22 05:10

    I know this is probably going to cause an unnecessary flame war, but I can see how you might want more than one database connection, so I would concede the point that singleton might not be the best solution for that... however, there are other uses of the singleton pattern that I find extremely useful.

    Here's an example: I decided to roll my own MVC and templating engine because I wanted something really lightweight. However, the data that I want to display contains a lot of special math characters such as ≥ and μ and what have you... The data is stored as the actual UTF-8 character in my database rather than pre-HTML-encoded because my app can deliver other formats such as PDF and CSV in addition to HTML. The appropriate place to format for HTML is inside the template ("view" if you will) that is responsible for rendering that page section (snippet). I want to convert them to their appropriate HTML entities, but PHPs get_html_translation_table() function is not super fast. It makes better sense to retrieve the data one time and store as an array, making it available for all to use. Here's a sample I knocked together to test the speed. Presumably, this would work regardless of whether the other methods you use (after getting the instance) were static or not.

    class EncodeHTMLEntities {
    
        private static $instance = null;//stores the instance of self
        private $r = null;//array of chars elligalbe for replacement
    
        private function __clone(){
        }//disable cloning, no reason to clone
    
        private function __construct()
        {
            $allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
            $specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
            $this->r = array_diff($allEntities, $specialEntities);
        }
    
        public static function replace($string)
        {
            if(!(self::$instance instanceof self) ){
                self::$instance = new self();
            }
            return strtr($string, self::$instance->r);
        }
    }
    //test one million encodings of a string
    $start = microtime(true);
    for($x=0; $x<1000000; $x++){
        $dump = EncodeHTMLEntities::replace("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)");
    }
    $end = microtime(true);
    echo "Run time: ".($end-$start)." seconds using singleton\n";
    //now repeat the same without using singleton
    $start = microtime(true);
    for($x=0; $x<1000000; $x++){
        $allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
        $specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
        $r = array_diff($allEntities, $specialEntities);
        $dump = strtr("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)", $r);
    }
    $end = microtime(true);
    echo "Run time: ".($end-$start)." seconds without using singleton";
    

    Basically, I saw typical results like this:

    php test.php
    Run time: 27.842966794968 seconds using singleton
    Run time: 237.78191494942 seconds without using singleton
    

    So while I'm certainly no expert, I don't see a more convenient and reliable way to reduce the overhead of slow calls for some kind of data, while making it super simple (single line of code to do what you need). Granted my example only has one useful method, and therefore is no better than a globally defined function, but as soon as you have two methods, you're going to want to group them together, right? Am I way off base?

    Also, I prefer examples that actually DO something, since sometimes it's hard to visualise when an example includes statements like "//do something useful here" which I see all the time when searching for tutorials.

    Anyway, I'd love any feedback or comments on why using a singleton for this type of thing is detrimental (or overly complicated).

    0 讨论(0)
  • 2020-11-22 05:10

    Quick example:

    final class Singleton
    {
        private static $instance = null;
    
        private function __construct(){}
    
        private function __clone(){}
    
        private function __wakeup(){}
    
        public static function get_instance()
        {
            if ( static::$instance === null ) {
                static::$instance = new static();
            }
            return static::$instance;
        }
    }
    

    Hope help.

    0 讨论(0)
提交回复
热议问题