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


当前回答

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

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

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

现在你可以做:

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

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

其他回答

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

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

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

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

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

private function __clone() {}

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

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

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

/**
 * Singleton class
 *
 */
final class UserFactory
{
    private static $inst = null;

    // Prevent cloning and de-serializing
    private function __clone(){}
    private function __wakeup(){}


    /**
     * Call this method to get singleton
     *
     * @return UserFactory
     */
    public static function Instance()
    {
        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;

But:

$fact = new UserFactory()

抛出错误。

参见http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static了解静态变量的作用域以及为什么设置static $inst = null;的工作原理。

这篇文章涵盖了相当广泛的主题: 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.