在PHP5中创buildSingletondevise模式

如何使用PHP5类创build一个Singleton类?

/** * 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以了解静态variables作用域以及为什么设置;static $inst = null; 作品。

PHP 5.3允许通过后期的静态绑定来创build一个可inheritance的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; } } 

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

现在你可以做:

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

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

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

这是一个正确的可inheritance的Singleton基类。

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

testing代码:

 class Foo extends Singleton {} class Bar extends Singleton {} echo get_class(Foo::getInstance()) . "\n"; echo get_class(Bar::getInstance()) . "\n"; 

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

 private function __clone() {} 

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

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

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

真正的一个和现代的做单身模式的方法是:

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

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

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

这个代码可以申请任何类而不关心它的类名。

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

respose:

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

如果您使用的是PHP 5.4: 特征它的一个选项,所以你不必浪费inheritance层次结构为了有Singleton模式

并且还要注意,无论你使用traits还是扩展Singleton类,一个松散的结尾是创build子类的单例,如果你不添加下面的代码行:

  protected static $inst = null; 

在小孩class上

意想不到的结果是:

 object(DBFactoryChild)#1 (0) { } object(DBFactoryChild)#1 (0) { } 
 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(); 

支持多个对象每行1行:

这种方法将强制你想要的任何类的单身人士,你必须做的是添加1方法,你想成为一个单身人士,这将为你做。

这还将对象存储在“SingleTonBase”类中,以便可以通过recursionSingleTonBase对象来debugging您在系统中使用的所有对象。


创build一个名为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(); 

你可以在任何你所拥有的类中添加这个singleton函数,每个类只能创build一个实例。

注意:您应该总是使__construct专用于消除使用新的Class(); 实例。

对于我来说,所有这些复杂性(“后期静态绑定”… harumph)只是PHP的破坏对象/类模型的标志。 如果类对象是第一类对象(请参阅Python),那么“$ _instance”将是一个类实例variables – 类对象的成员,而不是其实例的成员/属性,而不是共享由其后裔。 在Smalltalk世界中,这是“类variables”和“类实例variables”之间的区别。

在PHP中,在我看来,我们需要认真考虑模式是编写代码的指南 – 我们可能会想到Singleton模板,但是试图编写从实际的“Singleton”类inheritance的代码看起来误导了PHP(虽然我认为一些进取的灵魂可以创build一个合适的SVN关键字)。

我将继续使用共享模板分别对每个单例进行编码。

请注意,我绝对保持在单独的讨论中,生活太短暂。

本文涵盖了相当广泛的话题: http : //www.phptherightway.com/pages/Design-Patterns.html#singleton

请注意以下几点:

  • 构造函数__construct()被声明为protected以防止通过new运算符在类之外创build新的实例。
  • 魔术方法__clone()被声明为private以防止通过clone操作符来clone类的实例。
  • 魔术方法__wakeup()被声明为private以防止通过全局函数unserialize()对类的实例进行反unserialize()
  • 通过静态创build方法getInstance()中的关键字static通过延迟静态绑定来创build新实例。 这允许在示例中class Singleton的子类化。

我知道这可能会导致一场不必要的火焰战争,但我可以看到你可能需要多个数据库连接,所以我会承认单身人士可能不是最好的解决scheme…但是,单身模式的其他用途,我觉得非常有用。

下面是一个例子:我决定推出我自己的MVC和模板引擎,因为我想要一些非常轻量级的东西。 但是,我要显示的数据包含许多特殊的math字符,例如≥和μ以及您有什么…数据在我的数据库中存储为实际的UTF-8字符,而不是预先HTML编码的除了HTML之外,我的应用还可以提供其他格式,例如PDF和CSV。 格式化HTML的适当位置在模板(如果您愿意的话)中,它负责渲染该页面部分(片段)。 我想将它们转换成适当的HTML实体,但是PHP的get_html_translation_table()函数并不是非常快。 一次检索数据并将其作为数组存储,使所有人都可以使用。 这里有一个样本我敲在一起来testing速度。 据推测,这将工作,无论你使用的其他方法(获得实例后)是否是静态的。

 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
运行时间:27.842966794968秒使用单身
运行时间:237.78191494942秒而不使用单例

所以虽然我当然不是专家,但是我并没有看到一种更方便,更可靠的方法来减less某些数据的慢速调用的开销,同时使它非常简单(单行代码来执行您所需要的)。 当然,我的例子只有一个有用的方法,因此并不比全局定义的函数更好,但只要有两种方法,你就想把它们组合在一起,对吧? 我离开基地?

另外,我更喜欢实际上做某事的例子,因为当一个例子包含像“//在这里做有用的事情”这样的语句时,有时很难直观地看到。

无论如何,我喜欢任何反馈或评论为什么使用单身这种事情是有害的(或过于复杂)。

我同意第一个答案,但是我也会宣布这个类是final的,所以不能延长,因为延长单例违反了单例模式。 此外,实例variables应该是私有的,以便它不能被直接访问。 还使__clone方法专用,以便您不能克隆单身人士对象。

以下是一些示例代码。

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

这应该是单身人士的正确方法。

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

我喜欢使用traits的jose-segura方法,但不喜欢在子类上定义一个静态variables。 下面是一个解决scheme,它通过将静态局部variables中的实例caching到由类名索引的工厂方法中来避免它:

 <?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相同,只需要子类中的静态variables。

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

  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

这是在Database类上创build单例的例子

devise模式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] 

仅使用单个实例不会创build3个实例

你并不需要使用Singleton模式,因为它被认为是一个反对模式。 基本上有很多理由不去实现这个模式。 阅读此开始: PHP单例类的最佳做法 。

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

这是我来解决这个问题。

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

只是为了certificate它的工作原理如下:

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

我写了很长时间以为在这里分享

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

这是我的例子,它提供了调用$ var = new Singleton()的能力,并且还创build了3个variables来testing它是否创build了新的对象:

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