一个人如何使用PHP5类创建Singleton类?
一个人如何使用PHP5类创建Singleton类?
Answers:
/**
* 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/zh-CN/language.variables.scope.php#language.variables.scope.static了解静态变量范围以及为什么设置static $inst = null;
有效。
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;
}
}
这就解决了问题,在PHP 5.3之前,任何扩展Singleton的类都将生成其父类的实例,而不是其父类的实例。
现在您可以执行以下操作:
class Foobar extends Singleton {};
$foo = Foobar::getInstance();
$ foo将是Foobar的实例,而不是Singleton的实例。
"subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());"
。
$instance
驻留在Singleton中,而不是子类中。在实例化某些子类之后,getInstance()将为所有子类返回该实例。
不幸的是,当有多个子类时,Inwdr的答案就中断了。
这是一个正确的可继承的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];
}
}
测试代码:
class Foo extends Singleton {}
class Bar extends Singleton {}
echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
制作Singleton模式的一种真实且现代的方法是:
<?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
如您所见,这种实现更加灵活。
instance
功能$instance
应该是null
没有false
您可能应该添加一个私有的__clone()方法来禁止实例的克隆。
private function __clone() {}
如果您不包括此方法,则可以进行以下操作
$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;
现在$inst1
!== $inst2
-它们不再是同一个实例。
<?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;
重新放置:
object(DBFactoryChild)#1 (0) {
}
object(DBFactory)#2 (0) {
}
如果您使用的是PHP 5.4:trait是它的一个选择,那么您不必浪费继承层次结构即可拥有Singleton模式
并且还请注意,如果不添加以下代码行,则是使用特征还是扩展Singleton类的一个松散结局是创建子类的Singleton:
protected static $inst = null;
在儿童班
意外的结果将是:
object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
此方法将对您希望的任何类强制执行单例,而您要做的就是向希望创建单例的类添加1个方法,这将为您完成。
这还将对象存储在“ SingleTonBase”类中,因此您可以通过递归SingleTonBase
对象来调试系统中已使用的所有对象。
创建一个名为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();
您可以只在您拥有的任何类中添加此单例函数,并且每个类仅创建1个实例。
注意:您应始终将__construct设为私有,以消除对new Class()的使用。实例化。
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();
您实际上不需要使用Singleton模式,因为它被视为反模式。基本上,有很多理由根本不执行此模式。首先阅读本节:PHP单例类的最佳实践。
如果毕竟您仍然认为需要使用Singleton模式,那么我们可以编写一个类,该类将通过扩展SingletonClassVendor抽象类来获得Singleton功能。
这就是我用来解决这个问题的方法。
<?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
}
}
只是为了证明它能按预期工作:
/**
* 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
对我而言,所有这些复杂性(“后期静态绑定” ...拙劣的理解)只是PHP损坏的对象/类模型的标志。如果类对象是一流的对象(请参阅Python),则“ $ _instance”将是一个类实例变量-类对象的成员,与其实例的成员/属性相对,也与共享的相对由其后代。在Smalltalk世界中,这是“类变量”和“类实例变量”之间的区别。
在PHP中,对我来说,似乎我们需要牢记模式是编写代码的指南-我们也许会考虑使用Singleton模板,但尝试编写继承自实际“ Singleton”类的代码对于PHP来说似乎是错误的(尽管我认为一些进取的人可以创建一个合适的SVN关键字)。
我将继续使用共享模板分别对每个单例进行编码。
请注意,我绝对不参加单例讨论,生活太短了。
我知道这可能会引起不必要的火焰战争,但是我可以看到您可能想要多个数据库连接,因此我要承认单身人士可能不是最好的解决方案……不过,我发现单例模式的其他用途非常有用。
这是一个例子:我决定推出自己的MVC和模板引擎,因为我想要真正轻巧的东西。但是,我要显示的数据包含许多特殊的数学字符(例如≥和μ)以及您所拥有的内容...数据以实际的UTF-8字符存储在我的数据库中,而不是预先进行HTML编码,因为除了HTML之外,我的应用程序还可以提供其他格式,例如PDF和CSV。HTML格式的适当位置是在模板内部(如果需要,则为“视图”),该模板负责呈现该页面部分(摘要)。我想将它们转换为适当的HTML实体,但是PHP的get_html_translation_table()函数并不是非常快。一次取回数据并将其存储为数组,使所有人都可以使用它,这更有意义。这里' 我将样品敲在一起测试速度。大概,不管您使用的其他方法(获取实例之后)是否是静态的,这都将起作用。
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秒,不使用单例
因此,尽管我当然不是专家,但我没有看到一种更便捷,更可靠的方法来减少对某种数据的缓慢调用的开销,同时又使其变得超级简单(只需一行代码即可完成所需的工作)。当然,我的示例只有一种有用的方法,因此它并不比全局定义的函数好,但是一旦您拥有两种方法,就将它们分组在一起,对吗?我离基地远吗?
另外,我更喜欢实际执行某些操作的示例,因为有时当示例中包含诸如“ //在此处做一些有用的事情”之类的语句时,很难看到它,我在搜索教程时一直会看到这些语句。
无论如何,我很喜欢关于为什么将单例用于这种类型的事情有害(或过于复杂)的任何反馈或评论。
本文相当广泛地涵盖了主题:http : //www.phptherightway.com/pages/Design-Patterns.html#singleton
请注意以下几点:
- 构造函数
__construct()
声明为protected
防止通过new
运算符在类外部创建新实例。__clone()
声明了magic方法,private
以防止通过clone
运算符克隆类的实例。__wakeup()
声明了magic方法,private
以防止通过全局函数对类的实例进行反序列化unserialize()
。- 通过
getInstance()
使用关键字的静态创建方法中的后期静态绑定来创建新实例static
。这允许class Singleton
示例中的子类化。
我写了很久很久的想法,想在这里分享
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();
我同意第一个答案,但我还要声明该类为final类,以便在扩展单例违反单例模式时无法扩展。另外,实例变量应该是私有的,这样就不能直接访问它。还要将__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 { }
这应该是Singleton的正确方法。
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(){
}
}
我喜欢使用@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相同,仅在子类中不需要静态变量。
检查是否有任何现有数据库实例的数据库类将返回先前的实例。
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类上创建单例的示例
设计模式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]
仅使用单个实例,不创建3个实例
快速示例:
final class Singleton
{
private static $instance = null;
private function __construct(){}
private function __clone(){}
private function __wakeup(){}
public static function get_instance()
{
if ( static::$instance === null ) {
static::$instance = new static();
}
return static::$instance;
}
}
希望对您有所帮助。
这是我的示例,该示例提供了调用$ var = new Singleton()的功能,还创建了3个变量来测试是否创建了新对象:
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();