代码之家  ›  专栏  ›  技术社区  ›  Andrew Moore

在PHP5中创建单例设计模式

  •  194
  • Andrew Moore  · 技术社区  · 17 年前

    如何使用PHP5类创建单例类?

    20 回复  |  直到 11 年前
        1
  •  270
  •   Arount    8 年前
    /**
     * 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()
        {
    
        }
    }
    

    使用:

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

    $fact == $fact2;

    $fact = new UserFactory()
    

    抛出一个错误。

    看见 http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static static $inst = null; 作品

        2
  •  122
  •   Community Mohan Dere    9 年前

    不幸地 Inwdr's answer

    下面是一个正确的可继承单例基类。

    class Singleton
    {
        private static $instances = array();
        protected function __construct() {}
        protected function __clone() {}
        public function __wakeup()
        {
            throw new Exception("Cannot unserialize singleton");
        }
    
        public static function getInstance()
        {
            $cls = get_called_class(); // late-static-bound class name
            if (!isset(self::$instances[$cls])) {
                self::$instances[$cls] = new static;
            }
            return self::$instances[$cls];
        }
    }
    

    class Foo extends Singleton {}
    class Bar extends Singleton {}
    
    echo get_class(Foo::getInstance()) . "\n";
    echo get_class(Bar::getInstance()) . "\n";
    
        3
  •  120
  •   Clive    12 年前

    class Singleton
    {
        protected static $instance = null;
    
        protected function __construct()
        {
            //Thou shalt not construct that which is unconstructable!
        }
    
        protected function __clone()
        {
            //Me not like clones! Me smash clones!
        }
    
        public static function getInstance()
        {
            if (!isset(static::$instance)) {
                static::$instance = new static;
            }
            return static::$instance;
        }
    }
    

    这就解决了问题,在PHP5.3之前,任何扩展单例的类都会生成其父类的实例,而不是自己的实例。

    现在您可以执行以下操作:

    class Foobar extends Singleton {};
    $foo = Foobar::getInstance();
    

        4
  •  39
  •   Abraham Tugalov    8 年前

    真实与现代 制作单例模式的方法是:

    <?php
    
    /**
     * Singleton Pattern.
     * 
     * Modern implementation.
     */
    class Singleton
    {
        /**
         * Call this method to get singleton
         */
        public static function instance()
        {
          static $instance = false;
          if( $instance === false )
          {
            // Late static binding (PHP 5.3+)
            $instance = new static();
          }
    
          return $instance;
        }
    
        /**
         * Make constructor private, so nobody can call "new Class".
         */
        private 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() {}
    
    }
    

    所以现在你可以像这样使用它。

    <?php
    
    /**
     * Database.
     *
     * Inherited from Singleton, so it's now got singleton behavior.
     */
    class Database extends Singleton {
    
      protected $label;
    
      /**
       * Example of that singleton is working correctly.
       */
      public function setLabel($label)
      {
        $this->label = $label;
      }
    
      public function getLabel()
      {
        return $this->label;
      }
    
    }
    
    // create first instance
    $database = Database::instance();
    $database->setLabel('Abraham');
    echo $database->getLabel() . PHP_EOL;
    
    // now try to create other instance as well
    $other_db = Database::instance();
    echo $other_db->getLabel() . PHP_EOL; // Abraham
    
    $other_db->setLabel('Priler');
    echo $database->getLabel() . PHP_EOL; // Priler
    echo $other_db->getLabel() . PHP_EOL; // Priler
    

    正如您所看到的,这种实现更加灵活。

        5
  •  26
  •   Stefan Gehrig    17 年前

    您可能应该添加一个private _clone()方法来禁止克隆实例。

    private function __clone() {}
    

    如果不包括此方法,则可以使用以下方法

    $inst1=UserFactory::Instance(); // to stick with the example provided above
    $inst2=clone $inst1;
    

    现在 $inst1 !== $inst2 -它们不再是同一个实例。

        6
  •  12
  •   jose segura    13 年前
    <?php
    /**
     * Singleton patter in php
     **/
    trait SingletonTrait {
       protected static $inst = null;
    
      /**
       * call this method to get instance
       **/
       public static function getInstance(){
          if (static::$inst === null){
             static::$inst = new static();
          }
          return static::$inst;
      }
    
      /**
       * protected to prevent clonning 
       **/
      protected function __clone(){
      }
    
      /**
       * protected so no one else can instance it 
       **/
      protected function __construct(){
      }
    }
    

    使用:

    /**
     *  example of class definitions using SingletonTrait
     */
    class DBFactory {
      /**
       * we are adding the trait here 
       **/
       use SingletonTrait;
    
      /**
       * This class will have a single db connection as an example
       **/
      protected $db;
    
    
     /**
      * as an example we will create a PDO connection
      **/
      protected function __construct(){
        $this->db = 
            new PDO('mysql:dbname=foodb;port=3305;host=127.0.0.1','foouser','foopass');
      }
    }
    class DBFactoryChild extends DBFactory {
      /**
       * we repeating the inst so that it will differentiate it
       * from UserFactory singleton
       **/
       protected static $inst = null;
    }
    
    
    /**
     * example of instanciating the classes
     */
    $uf0 = DBFactoryChild::getInstance();
    var_dump($uf0);
    $uf1 = DBFactory::getInstance();
    var_dump($uf1);
    echo $uf0 === $uf1;
    

    答复:

    object(DBFactoryChild)#1 (0) {
    }
    object(DBFactory)#2 (0) {
    }
    

    如果您使用的是PHP 5.4: 特质 这是一个选项,因此您不必浪费继承层次结构来获得 单例模式

    还要注意的是,您是否使用 扩展单例

       protected static $inst = null;
    

    在儿童班

    意外结果将是:

    object(DBFactoryChild)#1 (0) {
    }
    object(DBFactoryChild)#1 (0) {
    }
    
        7
  •  10
  •   hungneox    14 年前
    protected  static $_instance;
    
    public static function getInstance()
    {
        if(is_null(self::$_instance))
        {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
    

        8
  •  8
  •   Community Mohan Dere    6 年前

    此方法将在您希望的任何类上强制执行singleton,您所要做的就是向您希望创建singleton的类添加1个方法,这将为您做到这一点。

    SingleTonBase 物体。


    创建一个名为SingletonBase.php的文件,并将其包含在脚本的根目录中!

    abstract class SingletonBase
    {
        private static $storage = array();
    
        public static function Singleton($class)
        {
            if(in_array($class,self::$storage))
            {
                return self::$storage[$class];
            }
            return self::$storage[$class] = new $class();
        }
        public static function storage()
        {
           return self::$storage;
        }
    }
    

    然后,对于任何想要创建单例的类,只需添加这个小的单例方法。

    public static function Singleton()
    {
        return SingletonBase::Singleton(get_class());
    }
    

    下面是一个小例子:

    include 'libraries/SingletonBase.resource.php';
    
    class Database
    {
        //Add that singleton function.
        public static function Singleton()
        {
            return SingletonBase::Singleton(get_class());
        }
    
        public function run()
        {
            echo 'running...';
        }
    }
    
    $Database = Database::Singleton();
    
    $Database->run();
    

    你可以在任何一个类中添加这个单例函数,每个类只创建一个实例。

    注意:您应该始终将_构造设置为私有,以避免使用新类();实例化。

        9
  •  5
  •   rizon    13 年前
    class Database{
    
            //variable to hold db connection
            private $db;
            //note we used static variable,beacuse an instance cannot be used to refer this
            public static $instance;
    
            //note constructor is private so that classcannot be instantiated
            private function __construct(){
              //code connect to database  
    
             }     
    
             //to prevent loop hole in PHP so that the class cannot be cloned
            private function __clone() {}
    
            //used static function so that, this can be called from other classes
            public static function getInstance(){
    
                if( !(self::$instance instanceof self) ){
                    self::$instance = new self();           
                }
                 return self::$instance;
            }
    
    
            public function query($sql){
                //code to run the query
            }
    
        }
    
    
    Access the method getInstance using
    $db = Singleton::getInstance();
    $db->query();
    
        10
  •  5
  •   VXp Kadir BuÅ¡atlić    8 年前

    您实际上不需要使用单例模式,因为它被认为是一种反模式。基本上有很多理由根本不实施这种模式。请阅读以下内容: Best practice on PHP singleton classes .

    如果您仍然认为需要使用Singleton模式,那么我们可以编写一个类,通过扩展SingletonClassVendor抽象类来获得Singleton功能。

    <?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() {}
    
    }
    

    使用示例:

    /**
     * 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
        }
    }
    

    为了证明它按预期工作:

    /**
     *  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
    
        11
  •  3
  •   Tom Stambaugh    12 年前

    对我来说,所有这些复杂性(“后期静态绑定”…harumph)只是PHP对象/类模型崩溃的标志。如果类对象是第一类对象(参见Python),那么“$\u实例”将是一个类

    在PHP中,在我看来,似乎我们需要牢记模式是编写代码的指南——我们可能会考虑使用单例模板,但尝试编写从实际“单例”类继承的代码对于PHP来说似乎是错误的(尽管我认为一些有进取心的人可以创建合适的SVN关键字)。

    请注意,我绝对不参与单身是邪恶的讨论,生命太短暂了。

        12
  •  3
  •   Community Mohan Dere    9 年前

    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";
    

    基本上,我看到了这样的典型结果:

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

    因此,虽然我当然不是专家,但我看不到一种更方便、更可靠的方法来减少对某种数据的缓慢调用的开销,同时使其变得超级简单(只需一行代码即可完成所需操作)。假设我的例子只有一个有用的方法,因此并不比一个全局定义的函数好,但是一旦你有了两个方法,你就会想把它们组合在一起,对吗?我离基地很远吗?

    另外,我更喜欢实际做一些事情的例子,因为有时候很难想象一个例子包含“//这里做一些有用的事情”这样的语句,我在搜索教程时经常看到这些语句。

    不管怎么说,我希望得到任何反馈或评论,说明为什么在这种情况下使用单例是有害的(或过于复杂)。

        13
  •  1
  •   Krzysztof Przygoda    10 年前

    http://www.phptherightway.com/pages/Design-Patterns.html#singleton

    注意以下几点:

    • 构造器 __construct() 声明为 protected new 操作人员
    • 神奇的方法 __clone() 声明为 private 阻止通过 clone
    • __wakeup() 私有的 unserialize()
    • 通过静态创建方法中的后期静态绑定创建新实例 getInstance() 使用关键字 static 允许对 class Singleton 在这个例子中。
        14
  •  1
  •   Gyaneshwar Pardhi    9 年前

    我很久以前就写过想在这里分享的东西

    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();
    
        15
  •  0
  •   Joseph Crawford    12 年前

    下面是一些示例代码。

    /**
     * Singleton class
     *
     */
    final class UserFactory
    {
        private static $_instance = null;
    
        /**
         * Private constructor
         *
         */
        private function __construct() {}
    
        /**
         * Private clone method
         *
         */
         private function __clone() {}
    
        /**
         * Call this method to get singleton
         *
         * @return UserFactory
         */
        public static function getInstance()
        {
            if (self::$_instance === null) {
                self::$_instance = new UserFactory();
            }
            return self::$_instance;
        }
    }
    

    $user_factory = UserFactory::getInstance();
    

    这会阻止你做什么(这会违反单例模式.)。。

    $user_factory = UserFactory::$_instance;
    
    class SecondUserFactory extends UserFactory { }
    
        16
  •  0
  •   Mário Kapusta    12 年前

    这应该是单身的正确方式。

    class Singleton {
    
        private static $instance;
        private $count = 0;
    
        protected function __construct(){
    
        }
    
        public static function singleton(){
    
            if (!isset(self::$instance)) {
    
                self::$instance = new Singleton;
    
            }
    
            return self::$instance;
    
        }
    
        public function increment()
        {
            return $this->count++;
        }
    
        protected function __clone(){
    
        }
    
        protected function __wakeup(){
    
        }
    
    } 
    
        17
  •  0
  •   Eric Anderson    12 年前

    我喜欢使用traits的@josesegura方法,但不喜欢在子类上定义静态变量。下面是一个解决方案,它通过将静态局部变量中的实例缓存到按类名索引的工厂方法来避免这种情况:

    <?php
    trait Singleton {
    
      # Single point of entry for creating a new instance. For a given
      # class always returns the same instance.
      public static function instance(){
        static $instances = array();
        $class = get_called_class();
        if( !isset($instances[$class]) ) $instances[$class] = new $class();
        return $instances[$class];
      }
    
      # Kill traditional methods of creating new instances
      protected function __clone() {}
      protected function __construct() {}
    }
    

    用法与@jose segura相同,只是子类中不需要静态变量。

        18
  •  0
  •   sunil rajput    11 年前

    数据库类,用于检查是否存在任何现有数据库实例,它将返回以前的实例。

       class Database {  
            public static $instance;  
             public static function getInstance(){  
                if(!isset(Database::$instance) ) {  
                    Database::$instance = new Database();  
                }  
               return Database::$instance;  
             }  
             private function __cunstruct() {  
               /* private and cant create multiple objects */  
             }  
             public function getQuery(){  
                return "Test Query Data";  
             }  
        }  
        $dbObj = Database::getInstance();  
        $dbObj2 = Database::getInstance();  
        var_dump($dbObj);  
        var_dump($dbObj2);  
    
    
    /* 
    After execution you will get following output: 
    
    object(Database)[1] 
    object(Database)[1] 
    
    */  
    

    裁判 http://www.phptechi.com/php-singleton-design-patterns-example.html

        19
  •  0
  •   Surendra Kumar Ahir    10 年前

    这是在数据库类上创建singleton的示例

    设计模式 1) 独生子女

    class Database{
      public static $instance;
      public static function getInstance(){
        if(!isset(Database::$instance)){
        Database::$instance=new Database();
    
         return Database::$instance;
        }
    
      }
    
      $db=Database::getInstance();
      $db2=Database::getInstance();
      $db3=Database::getInstance();
    
      var_dump($db);
      var_dump($db2);
      var_dump($db3);
    

    然后就出局了--

      object(Database)[1]
      object(Database)[1]
      object(Database)[1]
    

    仅使用单个实例,不创建3个实例

        20
  •  0
  •   Dmitry Leiko    6 年前

    快速示例:

    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;
        }
    }
    

    希望能有所帮助。

        21
  •  -4
  •   bboydev    13 年前

    下面是我的示例,它提供了调用$var=new Singleton()的功能,还可以创建3个变量来测试它是否创建了新对象:

    class Singleton{
    
        private static $data;
    
        function __construct(){
            if ($this::$data == null){
                $this->makeSingleton();
            }
            echo "<br/>".$this::$data;
        }
    
        private function makeSingleton(){
            $this::$data = rand(0, 100);
        }
    
        public function change($new_val){
            $this::$data = $new_val;
        }
    
        public function printme(){
            echo "<br/>".$this::$data;
        }
    
    }
    
    
    $a = new Singleton();
    $b = new Singleton();
    $c = new Singleton();
    
    $a->change(-2);
    $a->printme();
    $b->printme();
    
    $d = new Singleton();
    $d->printme();