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


204

Как создать класс Singleton, используя классы PHP5?



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

3
@ Андрей Ммммкей. Без обид, но я предлагаю вам получить книгу о качестве программного обеспечения, прежде чем мы продолжим это обсуждение. Синглтоны не упрощают, а усложняют нормальное обслуживание и развитие. На самом деле все наоборот: это юнит-тесты, которые в первую очередь упрощают и делают возможной разработку.
Гордон

3
@Andrew: Теперь вы предполагаете, что вам нужно только одно соединение с базой данных. Что происходит, когда ваши требования меняются, и вам действительно нужно общаться с 2 серверами баз данных? Не говоря уже о том, что если вы не можете доверять своей команде, чтобы все было правильно , создание синглтона не поможет вам ни в малейшей степени. Делайте все правильно с самого начала и получите команду, которой вы можете доверять, и у вас все будет хорошо.
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 в null при каждом вызове Instance ()? В java переменная $ inst будет закрытым статическим атрибутом, который не следует сбрасывать снова и снова, иначе вы не могли бы сделать его одиночным.
Руди Гарсия

8
Вот хорошее описание того, почему и как объявление переменной как статической в ​​функции работает так, как намеревается автор: php.net/manual/en/…
hereswhatidid

10
Вы должны использовать $ inst = new self (); не $ inst = new UserFactory (); для тех, кто сталкивается с этим позже. +1 за использование встроенной методологии PHP.
Ligemer

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

Настоящий и Современный способ сделать Singleton Pattern это:

<?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: trait его опция, поэтому вам не нужно тратить иерархию наследования, чтобы получить шаблон 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 приватным, чтобы исключить использование new 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

Когда я читал больше ответов, я имел в виду нечто подобное. К счастью, это было уже здесь :)
Ненависть

3

Вся эта сложность («позднее статическое связывание» ... харумф) для меня просто признак неработающей модели объекта / класса в PHP. Если бы объекты класса были объектами первого класса (см. Python), то «$ _instance» была бы переменной экземпляра класса - членом объекта класса, в отличие от элемента / свойства его экземпляров, а также в отличие от общего его потомками. В мире Smalltalk, это разница между «переменной класса» и «переменной экземпляра класса».

В PHP мне кажется, что нам нужно принять к сведению, что шаблоны являются руководством к написанию кода - возможно, мы можем подумать о шаблоне Singleton, но пытаемся написать код, который наследуется от реального класса «Singleton» выглядит неправильно для PHP (хотя я предположил, что какая-то предприимчивая душа могла бы создать подходящее ключевое слово SVN).

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

Заметьте, что я абсолютно не в курсе дискуссии о злодеях, жизнь слишком коротка.


Ваши замечания правильны при просмотре все возрастающей сложности языка PHP. Кажется, что добавляется слишком много новых ключевых слов, чтобы вырваться из слишком большого количества дыр в дизайне в слишком многих различных парадигмах кодирования. Хуже того, из-за высокой скорости изменений и несоответствия версий между хостами и платформами разработки сегодняшнее «решение для путешествия» (как в ответе @Eric Anderson [ stackoverflow.com/a/23998306/3696363] ) не работает в производственных системах, которые могут использовать «стабильную» версию вместо «последней, самой лучшей».
Элиягу Скочилас

2

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

Вот пример: я решил сделать свой собственный MVC и шаблонизатор, потому что хотел что-то действительно легкое. Однако данные, которые я хочу отобразить, содержат много специальных математических символов, таких как ≥ и μ, и что у вас ... Данные хранятся в моей базе данных как фактический символ UTF-8, а не в кодировке, предварительно закодированной в HTML, поскольку мое приложение может доставлять другие форматы, такие как PDF и CSV в дополнение к HTML. Подходящее место для форматирования 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 секунд без использования синглтона

Поэтому, хотя я, конечно, не эксперт, я не вижу более удобного и надежного способа уменьшить накладные расходы на медленные вызовы для каких-либо данных, в то же время делая его очень простым (одна строка кода, чтобы сделать то, что вам нужно). Конечно, в моем примере есть только один полезный метод, и поэтому он ничуть не лучше глобальной функции, но как только у вас появятся два метода, вы захотите сгруппировать их, верно? Я далеко от базы?

Кроме того, я предпочитаю примеры, которые на самом деле что-то делают, поскольку иногда трудно визуализировать, когда пример включает в себя такие выражения, как «// сделать что-то полезное здесь», которые я вижу все время при поиске учебников.

В любом случае, я хотел бы получить любые отзывы или комментарии о том, почему использование синглтона для такого типа вещей вредно (или слишком сложно).


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] 

*/  

Ссылка http://www.phptechi.com/php-singleton-design-patterns-example.html


0

Это пример создания синглтона в классе базы данных.

шаблоны проектирования 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.