如何使用PHP5类创建单例类?
当前回答
protected static $_instance;
public static function getInstance()
{
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
这段代码可以应用于任何类,而无需关心类名。
其他回答
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
正如你所看到的,这种实现更加灵活。
您实际上不需要使用单例模式,因为它被认为是反模式。基本上,有很多理由根本不实现这种模式。首先阅读:PHP单例类的最佳实践。
如果你仍然认为你需要使用单例模式,那么我们可以写一个类,允许我们通过扩展我们的SingletonClassVendor抽象类来获得单例功能。
这就是我解决这个问题的方法。
<?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
protected static $_instance;
public static function getInstance()
{
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
这段代码可以应用于任何类,而无需关心类名。