php 在 PHP5 中创建单例设计模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/203336/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:55:39  来源:igfitidea点击:

Creating the Singleton design pattern in PHP5

phpoopdesign-patternssingleton

提问by Andrew Moore

How would one create a Singleton class using PHP5 classes?

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

回答by

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

$fact == $fact2;

But:

但:

$fact = new UserFactory()

Throws an error.

抛出错误。

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

请参阅http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static以了解静态变量范围以及设置为何static $inst = null;有效。

回答by selfawaresoup

PHP 5.3 allows the creation of an inheritable Singleton class via late static binding:

PHP 5.3 允许通过后期静态绑定创建可继承的 Singleton 类:

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

This solves the problem, that prior to PHP 5.3 any class that extended a Singleton would produce an instance of its parent class instead of its own.

这解决了这个问题,在 PHP 5.3 之前,任何扩展 Singleton 的类都将生成其父类的实例而不是它自己的实例。

Now you can do:

现在你可以这样做:

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

And $foo will be an instance of Foobar instead of an instance of Singleton.

而 $foo 将是 Foobar 的一个实例,而不是 Singleton 的一个实例。

回答by mpartel

Unfortunately Inwdr's answerbreaks when there are multiple subclasses.

不幸的是,当有多个子类时,Inwdr 的答案会中断。

Here is a correct inheritable Singleton base class.

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

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

Test code:

测试代码:

class Foo extends Singleton {}
class Bar extends Singleton {}

echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";

回答by Abraham Tugalov

The Real One and Modernway to make Singleton Pattern is:

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

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

}

So now you can use it like.

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

<?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

As you see this realization is lot more flexible.

如您所见,这种实现更加灵活。

回答by Stefan Gehrig

You probably should add a private __clone() method to disallow cloning of an instance.

您可能应该添加一个私有的 __clone() 方法来禁止克隆实例。

private function __clone() {}

If you don't include this method the following gets possible

如果您不包含此方法,则可能会出现以下情况

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

now $inst1!== $inst2- they are not the same instance any more.

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

回答by jose segura

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

to use:

使用:

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

respose:

回应:

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

If you are using PHP 5.4: traitits an option, so you don't have to waste the inheritance hierarchy in order to have the Singleton pattern

如果您使用的是 PHP 5.4: trait它是一个选项,所以您不必为了拥有单例模式而浪费继承层次结构

and also notice that whether you use traitsor extends Singletonclass one loose end was to create singleton of child classes if you dont add the following line of code:

还要注意,如果不添加以下代码行,无论您使用特征还是扩展单例类,一个松散的结局是创建子类的单例:

   protected static $inst = null;

in the child class

在儿童班

the unexpected result will be:

意想不到的结果将是:

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

回答by hungneox

protected  static $_instance;

public static function getInstance()
{
    if(is_null(self::$_instance))
    {
        self::$_instance = new self();
    }
    return self::$_instance;
}

This code can apply for any class without caring about its class name.

此代码可以应用于任何类,而无需关心其类名。

回答by RobertPitt

Supports Multiple Objects with 1 line per class:

支持多个对象,每类 1 行:

This method will enforce singletons on any class you wish, al you have to do is add 1 method to the class you wish to make a singleton and this will do it for you.

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

This also stores objects in a "SingleTonBase" class so you can debug all your objects that you have used in your system by recursing the SingleTonBaseobjects.

这还将对象存储在“SingleTonBase”类中,因此您可以通过递归SingleTonBase对象来调试您在系统中使用的所有对象。



Create a file called SingletonBase.php and include it in root of your script!

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

The code is

代码是

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

Then for any class you want to make a singleton just add this small single method.

然后对于任何你想要制作单例的类,只需添加这个小的单一方法。

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


Here is a small example:

这是一个小例子:

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();

And you can just add this singleton function in any class you have and it will only create 1 instance per class.

你可以在你拥有的任何类中添加这个单例函数,它只会为每个类创建 1 个实例。

NOTE: You should always make the __construct private to eliminate the use of new Class(); instantiations.

注意:您应该始终将 __construct 设为私有以消除使用 new Class(); 实例化。

回答by rizon

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();

回答by DevWL

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.

你真的不需要使用单例模式,因为它被认为是一种反模式。基本上有很多理由根本不实施这种模式。阅读本文以开始:PHP 单例类的最佳实践

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.

如果毕竟你仍然认为你需要使用单例模式,那么我们可以编写一个类,通过扩展我们的 SingletonClassVendor 抽象类来允许我们获得单例功能。

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