Створення шаблону дизайну Singleton в PHP5


204

Як можна створити клас Singleton, використовуючи класи PHP5?



1
@Andrew Не інстанціюйте другий екземпляр, що з'єднується з базою даних. Передайте цей екземпляр туди, де це потрібно. Необхідність Сінглтона - це кодовий запах. Більше на gooh.posterous.com/singletons-in-php
Гордон

3
@Andrew Mmmmkay. Без образи, але я пропоную вам отримати книгу про якість програмного забезпечення, перш ніж ми продовжимо цю дискусію. Сінглтон не спрощує, але ускладнює нормальне обслуговування та розвиток. Насправді все навпаки: це тести, що спрощують і в першу чергу дозволяють розвивати.
Гордон

3
@Andrew: Ви припускаєте, що вам потрібно лише одне підключення до бази даних. Що відбувається, коли ваші вимоги змінюються, і вам насправді потрібно поговорити з двома серверами баз даних? Не кажучи вже про те, якщо ви не можете довіряти вашій команді робити все правильно , створити сингл не допоможе вам. Робіть справи з самого початку і знайдіть команду, якій ви можете довіряти, і ви будете добре.
ircmaxell

4
Тільки тому, що Сінглтон був зловживав, це не робить його поганим зразком, якого слід уникати. Не ненавиджу Сінглтона. Іноді це ідеально вдале рішення певної проблеми. Краще почніть міркувати, чому ми не повинні використовувати його, а не просто намагатися деградувати.
Жиль Лесіре

Відповіді:


268
/**
 * 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;працює.


59
для порівняння двох екземплярів слід використовувати ===, а не ==. == поверне true, якщо $ fact1 і $ fact2 обидва того ж класу, але === повертає true, якщо вони обидва є одним і тим же екземпляром одного і того ж об'єкта.
Кіт Тумблі

10
метод клонування також повинен бути приватним
Алекс Петров

22
Чи не вдасться цей метод скинути примірник UserFactory до нуля кожного разу, коли ви викликаєте Instance ()? У Java змінну $ inst буде приватним статичним атрибутом, який не слід скидати знову і знову, інакше ви можете також не зробити його однократним.
Руді Гарсія

8
Ось добре запишіть, чому і як оголошення змінної як статичної у функції працює, як автор має намір: php.net/manual/en/…
hereswhatidid

10
Ви повинні використовувати $ inst = new self (); не $ inst = новий UserFactory (); для тих, хто стикається з цим пізніше. +1 за використання вбудованої методології PHP
Лігемер

119

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.


1
Пізнє статичне зв'язування дійсно дуже добре в php 5.3. Шкода, що я досі не можу ним користуватися.
AntonioCS

4
Від @ggsonic : "subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());".
Брок Адамс

4
Це зовсім не спрацьовує, просто так буває, що Foobar був першим класом, який ви сконструювали?
Кріс КЛ

1
ще можливість клонувати ..... "" $ a = Singleton :: getInstance (); $ b = unserialize (serialize ($ a)); $ a! == $ b; "
бортунак

15
Це не працює, якщо є більше одного підкласу! $instanceпроживає в Сінглтоні, а не в підкласі. Після того, як деякий підклас буде ініційований, getInstance () поверне цей екземпляр для всіх підкласів.
mpartel

116

На жаль , відповідь 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";

1
Це поки що найближче до правильної реалізації Singleton. Також слід розглянути можливість введення методу __wakeup (), щоб запобігти несеріалізації.
Роберт Россманн

Насправді вам доведеться або викинути Виняток, або підняти помилку вручну - оголошення функції захищеною / приватною призведе лише до E_WARNING, що не може отримати доступ до методу, але в іншому випадку продовжить.
Роберт Россманн

Дякую. У мене зазвичай всі попередження тощо перетворюються на винятки, тому я забув про різницю, коли тестував: P
mpartel

Це єдине рішення, яке я знайшов, що належним чином стосується кількох підкласів. Дякую!
Боб Данкерт

36

Справжній і сучасний спосіб зробити однотонний візерунок:

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

Як бачите, ця реалізація набагато гнучкіша.


4
Це найясніша відповідь щодо візерунка Сінглтона в цій темі. Дякую.
Гас

Я реалізував цей підхід, і він працює як слід: другий екземпляр стає нульовим. Однак і мені не потрібно було розширювати конкретний клас. Я щойно реалізував Singleton :: instance () у конструкторі цього конкретного класу.
snaphuman

в instanceфункції $instanceповинні бути nullНЕfalse
Mifas

Так, але це не функція, а метод.
Авраам Тугалов

26

Ймовірно, слід додати приватний метод __clone (), щоб заборонити клонування екземпляра.

private function __clone() {}

Якщо ви не включите цей метод, можливе наступне

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

зараз $inst1! == $inst2- вони вже не той самий екземпляр.


11
<?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) {
}

10
protected  static $_instance;

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

Цей код може застосовуватися для будь-якого класу, не піклуючись про його клас.


8

Підтримує кілька об’єктів з 1 рядком на клас:

Цей метод примусить одиночні клавіші до будь-якого класу, який ви хочете, але вам потрібно зробити це додати 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 (); моменти.


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

5

Вам не потрібно використовувати шаблон 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

Читаючи більш схвалені відповіді, я мав на увазі щось подібне. На щастя, це було вже тут :)
hatef

3

Вся ця складність ("пізня статична прив'язка" ... harumph) для мене просто ознака розбитої моделі об'єкта / класу PHP. Якщо об'єкти класу були об'єктами першого класу (див. Python), то "$ _instanca" була б змінною екземпляра класу - членом об'єкта класу, на відміну від члена / властивості його екземплярів, а також на відміну від загальнодоступних її нащадками. У світі Smalltalk це різниця між "змінною класу" та "змінною екземпляра класу".

У PHP мені здається, що нам потрібно прийняти до душі вказівки, що шаблони є керівництвом щодо написання коду - ми, можливо, можемо подумати про шаблон Singleton, але намагаємось написати код, який успадковується від фактичного класу "Singleton" виглядає неправильно для PHP (хоча я гадаю, що якась заповзятлива душа може створити відповідне ключове слово SVN).

Я продовжую просто кодувати кожен сингтон окремо, використовуючи загальний шаблон.

Зауважте, що я абсолютно не заважаю на дискусії одинаків - це зло, життя занадто коротке.


Ваші зауваження потрібні, коли переглядаєте постійно зростаючу складність мови PHP. Здається, що занадто багато нових ключових слів додаються, щоб виправити наш занадто багато різних дизайнерських отворів у занадто великій кількості парадигм кодування. Що ще гірше, через високу швидкість змін та перекос версій на хостах та платформах розробки сьогоднішнє "рішення ду пу" (як риси у відповіді @Eric Anderson [ stackoverflow.com/a/23998306/3696363] ) не працює на виробничих системах, які, можливо, мають "стабільну" версію замість "останньої, найбільшої".
Eliyahu Skoczylas

2

Я знаю, що це, мабуть, спричинить зайву полум’яну війну, але я бачу, як ви можете хотіти більше ніж одне підключення до бази даних, тож я б визнав, що синглтон може бути не найкращим рішенням для цього ... однак, є інші способи використання однотонної картини, які я вважаю надзвичайно корисними.

Ось приклад: я вирішив прокатати власний двигун 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 секунд без використання синглтона

Тож, хоча я, звичайно, не є експертом, я не бачу більш зручного і надійного способу зменшити накладні витрати повільних дзвінків на якусь інформацію, роблячи це надзвичайно простим (один рядок коду робити те, що потрібно). Наведений мій приклад має лише один корисний метод, і тому він не є кращим, ніж глобально визначена функція, але як тільки у вас є два методи, ви хочете згрупувати їх разом, правда? Чи я далеко від бази?

Також я віддаю перевагу прикладам, які насправді щось роблять, оскільки іноді важко уявити, коли приклад включає висловлювання на зразок "// зробити щось корисне тут", які я бачу весь час під час пошуку навчальних посібників.

У будь-якому разі, я хотів би отримати будь-які відгуки чи коментарі щодо того, чому використання синглів для такого типу речей є згубним (або надмірно складним).


1

Ця стаття досить широко висвітлює тему: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

Зверніть увагу на наступне:

  • Конструктор __construct()оголошено таким, protectedщо запобігає створенню нового екземпляра поза класом через newоператор.
  • Магічний метод __clone()оголошено таким чином, privateщоб запобігти клонуванню екземпляра класу через cloneоператор.
  • Магічний метод __wakeup()оголошений як privateзапобігання несеріалізації екземпляра класу за допомогою глобальної функції unserialize().
  • Новий екземпляр створюється за допомогою пізньої статичної прив'язки у методі створення статичного ключа getInstance()із ключовим словом static. Це дозволяє підкласирувати class Singletonв прикладі.

1

Я вже давно написав думки поділитися тут

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

0

Я погоджуюся з першою відповіддю, але я б також оголосив клас остаточним, щоб його не можна було продовжити, оскільки розширення сингтона порушує схему сингтона. Також змінна інстанція повинна бути приватною, щоб не можна було отримати прямий доступ до неї. Також зробіть метод __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 { }

0

Це має бути правильний шлях Сінглтона.

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

    }

} 

0

Мені сподобався @ 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, тільки немає необхідності в статичній змінній у підкласах.


0

Клас бази даних, який перевіряє, чи є якийсь існуючий екземпляр бази даних, він поверне попередній екземпляр.

   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


0

Це приклад створення синглтона в класі 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 екземпляри


0

Короткий приклад:

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

Надіюсь на допомогу.


-4

Ось мій приклад, який забезпечує можливість виклику як $ 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();

5
За винятком того, що це не сингтон. Можна створити кілька примірників класу Singleton.
Ендрю Мур

Я думаю, що це врешті-решт, тому що незалежно від того, який екземпляр впливає на клас Singleton, зміни є для всіх екземплярів Singleton. Я додав ще дві функції вище. Тепер спробуємо змінити дані в одному екземплярі та перевірити інші. Отже, чи не Сінглтон, а якщо ні - що неправильно?
ббойдев

5
Синглтон - це клас, який дозволяє лише один екземпляр себе. Створюючи кілька примірників, ви скасовуєте цей принцип.
Ендрю Мур
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.