如何使用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的一个实例。

其他回答

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

<?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相同,只是在子类中不需要静态变量。

/**
 * 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;的工作原理。

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

private function __clone() {}

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

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

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

以上答案都是可以的,但我还会再补充一些。

无论谁在2021年来到这里,我都将展示另一个使用单例模式类作为trait的例子,并在任何类中重用它。

<?php

namespace Akash;

trait Singleton
{
    /**
     * Singleton Instance
     *
     * @var Singleton
     */
    private static $instance;

    /**
     * Private Constructor
     *
     * We can't use the constructor to create an instance of the class
     *
     * @return void
     */
    private function __construct()
    {
        // Don't do anything, we don't want to be initialized
    }

    /**
     * Get the singleton instance
     *
     * @return Singleton
     */
    public static function getInstance()
    {
        if (!isset(self::$instance)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Private clone method to prevent cloning of the instance of the
     * Singleton instance.
     *
     * @return void
     */
    private function __clone()
    {
        // Don't do anything, we don't want to be cloned
    }

    /**
     * Private unserialize method to prevent unserializing of the Singleton
     * instance.
     *
     * @return void
     */
    private function __wakeup()
    {
        // Don't do anything, we don't want to be unserialized
    }
}

所以,像在任何课堂上一样轻松地使用它。假设,我们想在UserSeeder类中实现单例模式。

<?php

class UserSeeder
{
    use Singleton;

    /**
     * Seed Users
     *
     * @return void
     */
    public function seed()
    {
        echo 'Seeding...';
    }
}

数据库类,检查是否有任何现有的数据库实例,它将返回先前的实例。

   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] 

*/  

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