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
Creating the Singleton design pattern in PHP5
提问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

