Як можна створити клас Singleton, використовуючи класи PHP5?
Як можна створити клас Singleton, використовуючи класи PHP5?
Відповіді:
/**
* 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, щоб зрозуміти статичні області змінної та чому налаштування 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
проживає в Сінглтоні, а не в підкласі. Після того, як деякий підклас буде ініційований, 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";
Справжній і сучасний спосіб зробити однотонний візерунок:
<?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: позначте його опцією, тому вам не доведеться витрачати ієрархію спадкування, щоб мати шаблон 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 приватним, щоб виключити використання нового 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, ми можемо написати клас, який дозволить нам отримати функціональність Singleton, розширивши наш абстрактний клас 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
Вся ця складність ("пізня статична прив'язка" ... harumph) для мене просто ознака розбитої моделі об'єкта / класу PHP. Якщо об'єкти класу були об'єктами першого класу (див. Python), то "$ _instanca" була б змінною екземпляра класу - членом об'єкта класу, на відміну від члена / властивості його екземплярів, а також на відміну від загальнодоступних її нащадками. У світі Smalltalk це різниця між "змінною класу" та "змінною екземпляра класу".
У PHP мені здається, що нам потрібно прийняти до душі вказівки, що шаблони є керівництвом щодо написання коду - ми, можливо, можемо подумати про шаблон Singleton, але намагаємось написати код, який успадковується від фактичного класу "Singleton" виглядає неправильно для PHP (хоча я гадаю, що якась заповзятлива душа може створити відповідне ключове слово SVN).
Я продовжую просто кодувати кожен сингтон окремо, використовуючи загальний шаблон.
Зауважте, що я абсолютно не заважаю на дискусії одинаків - це зло, життя занадто коротке.
Я знаю, що це, мабуть, спричинить зайву полум’яну війну, але я бачу, як ви можете хотіти більше ніж одне підключення до бази даних, тож я б визнав, що синглтон може бути не найкращим рішенням для цього ... однак, є інші способи використання однотонної картини, які я вважаю надзвичайно корисними.
Ось приклад: я вирішив прокатати власний двигун MVC та шаблони, бо хотів чогось справді легкого. Однак, дані, які я хочу відобразити, містять багато спеціальних математичних символів, таких як ≥ та μ, і що у вас є ... Дані зберігаються як фактичний символ UTF-8 у моїй базі даних, а не заздалегідь кодованим HTML, оскільки мій додаток може додавати інші формати, такі як PDF та CSV на додаток до HTML. Відповідне місце для форматування HTML - це всередині шаблону ("перегляньте", якщо ви хочете), який відповідає за візуалізацію цього розділу сторінки (фрагмент). Я хочу перетворити їх у відповідні HTML-об'єкти, але PHP-функції get_html_translation_table () не надто швидко. Доцільніше одноразово отримати дані та зберегти як масив, зробивши їх доступними для всіх. Ось ' sa зразок Я збив разом, щоб перевірити швидкість. Імовірно, це може працювати незалежно від того, чи були інші методи, які ви використовуєте (після отримання екземпляра), статичними чи ні.
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()
оголошено таким чином,private
щоб запобігти клонуванню екземпляра класу черезclone
оператор.- Магічний метод
__wakeup()
оголошений як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();
Я погоджуюся з першою відповіддю, але я б також оголосив клас остаточним, щоб його не можна було продовжити, оскільки розширення сингтона порушує схему сингтона. Також змінна інстанція повинна бути приватною, щоб не можна було отримати прямий доступ до неї. Також зробіть метод __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(){
}
}
Мені сподобався @ 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]
*/
Ref 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();