Singleton в PHP на примере подключения к MySQL. Singleton в PHP на примере подключения к MySQL Singleton php примеры

Введение

Singleton – один из самых простых шаблонов для понимания. Основное назначение – гарантировать существование только одно экземпляра класса. Причиной обычно является следующее: требуется только один объект исходного класса и Вам необходимо, что бы объект был доступен в любом месте приложения, т.е. глобальный доступ.

В качестве примера может служить класс для хранения установочных параметров(Settings). Settings class – хороший пример шаблона Singleton, потому что его данные не измены (единственный путь изменения установочных параметров это редактирование файла установочных параметров) и часто используются в различных частях приложения. Кроме того, создание новых объектов класса Settings, где это необходимо, требует ресурсы, что расточительно, т.к. объекты будут идентичны.

Определение

Шаблон Singleton предполагает наличие статического метода для создания экземпляра класса, при обращении к которому возвращается ссылка на оригинальный объект.

Пример для PHP5

Пример для PHP5(без реализации конкретных методов класса Settings)
class Settings {
private $settings = array();
private static $_instance = null;
private function __construct() {
// приватный конструктор ограничивает реализацию getInstance ()
}
protected function __clone() {
// ограничивает клонирование объекта
}
static public function getInstance() {
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
public function import() {
// ...
}
public function get() {
// ...
}
}

Реализация шаблона Singleton

Ключoм реализации шаблона Singleton является статическая переменная, переменная чье значение остается неизменным при исполнении за ее приделами. Это позволяет сохранить объект оригинальным между вызовами статического метода Settings::getInstance(), и возвратить ссылку на него при каждом последующем вызове метода.
Имейте так же в виду, что конструктор, как правило, приватный. Что бы обеспечить использование всегда только одного объекта Settings мы должны ограничить доступ к конструктору, что бы при попытке создания нового объекта возникала ошибка. Так же следует иметь в виду, что данные ограничения не возможны в PHP4.

Начинающие программисты, столкнувшись с ООП, обязтельно натыкаются на необходимость использования (ну или хотя бы понимания) шаблонов проектирования. И это многих пугает. Мало того, что сама парадигма достаточно сложна для понимания, так еще и паттерны какие-то. И хотя они как раз и придуманы для облегчения использования парадигмы, на первых порах играют как раз обратную роль. А всё из-за того, что те, кто придумывают паттерны, давно в ООП как рыба в воде, соответственно и объясняют их со своей колокольни. И получается замкнутый круг. Не зная паттернов сложно грамотно использовать ООП парадигму, однако не зная ООП сложно понять эти самые паттерны.

А все как обычно крайне просто. Нужно только создать необходимую ассоциацию, как все встанет на свои места.

Вот сейчас я попробую объяснить пару шаблонов простым русским языком. Может что-либо и получится.

Начнем с синглтона. Singleton переводится с английского как "одиночка". Но это так, ремарка. Теперь перенесемся в реальную жизнь.

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

А теперь представьте, что вы эту корочку ненароком потеряли. Ну или она пришла в негодность, да мало ли. Вам не нужно снова учиться, не нужно сдавать экзамены. Это уже все есть пожизненно. Вам нужно просто придти в ГАИ и получить дубликат. Тоесть результат когда то произведенных вами действий.

Это и есть суть "синглтон", тоесть "один раз". Необходимые для получения прав действия (обучение и сдача экзаменов) мы производим один раз в жизни. А результатом можем пользоваться сколь угодно мгого раз.

Теперь дальше, ближе к программированию. Вообще этот паттерн не являются паттерном ООП, как многие ошибочно полагают. Это паттерны проектировния или design patterns. Их можно использовать и помимо ООП и даже классов, в обычных функциях. Вот, чтобы было понятнее, с них и начнем. Вот допустим такая простенькая функция:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Function mySingleton ()
{
$time = microtime (true );

Return $time ;
}

Echo mySingleton ();
echo "
" ;
sleep (1 );
echo mySingleton ();

Если зпустить этот код, второй результат будет на секунду отличаться от первого. Ну это и понятно, не требует пояснений. Но вот если сделать так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Function mySingleton ()
{
static $time ;

If(empty($time ))
$time = microtime (true );

Return $time ;
}

Echo mySingleton ();
echo "
" ;
sleep (1 );
echo mySingleton ();

то сколько бы раз теперь мы бы не запускали функцию, результат будет одинаковый. Тот, который был вычислен при первом обращении. Вот это и есть синглтон во всей своей красе, правда очень примитивный.

Ну а теперь непосредственно в ООП. Не будем уходить от темы, просто обернем первую функцию в класс:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

class mySingleton
{
public $time ;


{
$this -> time = microtime (true );
}
}

$obj = new mySingleton ();
echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = new mySingleton ();
echo $obj -> time ;

И тут, каждый раз, когда мы создем объект, свойство $time будет принимать новое значение. Это иногда очень вредно, особенно при коннекте к базе данных. Если его поместить в конструктор, то подключение к бд будет происходить при каждой инициализации, а для скрипта обычно этого бывает достаточно сделать только единожды.

Но вернемся к нашим бранам. Как же нам сделать синглтон из класса? Чтобы получать только один объект, сколько бы раз мы к нему не обращались. Такая возможнсть появилась вместе с появлением статических классов. Вернее с возможностью использовать два в одном. Обращться к классу, как к таковому (статика) и создвать на его основе объекты. Вот мы сейчас заставим наш класс инициализировать и возвращать собственный объект:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

Class mySingleton
{
public $time ;


{
return new self ;
}

Public function __construct ()
{
$this -> time = microtime (true );
}
}


echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Обратите внимание, снаружи мы получили объект без конструкции new , обртившись к статическому методу класса через двойное двоеточие. Однако объектом он от этого быть не перестал. Хотя и результат не изменился.

Однако мы же знаем, что объект можно легко поместить в переменную. И тем более в свойство. Ну а его можно объявить статичным. И вот мы плавно подошли к реализации синглтона. Делаем по образу и подобию нашей второй функции:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

class mySingleton
{
public $time ;
static private $instance ;

Public static function getInstance ()
{

Return self :: $instance ;
}

Public function __construct ()
{
$this -> time = microtime (true );
}
}

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Вот и все, посмотрите на результат. Мы имеем то, что было вычислено при инициализации первого объекта. Еще бы, ведь это он и есть. Теперь один единственный, сколько бы раз мы его не дергали. Однако он будет так себя вести только при условии, что мы будем получать объект через статический метод. Но ведь есть еще возможность создать объект и с помощью конструкции new . А значит все наши труды насмарку. Так вот, чтобы загарантироваться от подобных казусов, нужно объявить конструктор приватным. Тогда конструкция new вне клаасса вызовет фатальную ошибку. Ну и на всякий случай запретим клонирование от греха подальше.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

class mySingleton
{
public $time ;
static private $instance ;

Public static function getInstance ()
{
if(empty(self :: $instance ))
self :: $instance = new self ;

Return self :: $instance ;
}

Private function __construct ()
{
$this -> time = microtime (true );
}

Private function __clone (){}
}

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Ну вот и все, мы имеем одну из самых распространенных реализаций паттерна Singletone

Что можно добавить. Использовать этот паттерн нужно с большой осторожностью, у него есть несколько недостатков. Он сродни глобальной переменной, он нарушает принцип SRP ну и так далее. Это тема отдельной публикации.

Но иногда, особенно при упомянутой организации коннекта к серверу СУБД, бывает очень даже полезен.

Просто не забывайте принцип KISS и используйте все в меру.

В следующий раз постараюсь рассказать еще несколько "знаменитых" паттернов, а пока всё)))

Class Singleton { private static $PrS="init private"; public static $PuS="init public"; public static function PrS($A=false) { if($A!==false) self::$PrS=$A; else return self::$PrS; } public static function PuS($A=false) { if($A!==false) self::$PuS=$A; else return self::$PuS; } } echo Singleton::PrS(); echo "\n"; echo Singleton::PuS(); // выведет init private // init public echo "\n -- \n"; $D = new Singleton(); echo $D->PrS(); // также выведет init private echo "\n"; // init public echo $D->PuS(); echo "\n -- \n SET them all!"; // А вот здесь Singleton::PrS("changed private"); // меняем переменные класса Singleton::PuS("changed public"); // используя статическую ссылку echo "\n"; // и попробуем проверить их из "созданного" класса (хотя это просто ссылка копия) echo $D->PrS(); // разумеется, выведет: changed private echo "\n"; echo $D->PuS(); // changed public

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

Слово static для функции говорит о том, что в глобальной таблице при компиляции ей уже выделен адрес - жестко выделен. Так же и со статическими переменными - их адрес также статичен. А НЕстатические переменные (классы) не существуют в адресном пространстве, пока их не определят (оператором new). Обращаться некуда. Для статических адрес уже есть - и к нему (к переменной) можно обратиться всегда - someStaticClass::value

Хотите использовать статический класс для работы с БД - заведите внутри приватную статическую переменную DB_handler. Надо работать с несколькими соединениями (несколько БД) - заведите еще по необходимости. Можно даже соорудить нечто статического массива. Почему нет? Появится необходимость слишком извернуться - перепишите класс. Т.е. копии статических классов вообще не различаются (при изготовлении их оператором new), пока в них не появится хотя бы одна НЕстатическая переменная. Правда, и после этого различаться они будут только этой НЕстатической переменной. Правда, при этом, управлять этой переменной уже получится уже только из изготовленного класса.

Public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new Singleton; } return self::$instance; }

Вот этот кусок кода как раз и возвращает копию ссылки на адрес статического класса Singleton. Это то же самое, что написать Singleton::(и там что-то)

Вот об этом и был вопрос - "ЗАЧЕМ?". Ответ простой - да, НЕЗАЧЕМ:)

Наверное, есть задачи, где надо заводить экземпляр класса Singleton, "...а вот если не было обращений (ну не потребовалось что-то), то ничего не заведется и все будет тихо и спокойно... А вот вроде как статический класс будет существовать даже тогда, когда он может не понадобиться... и ух, как страшно съест памяти... " В общем, как-то я не могу вот так с ходу придумать такой задачи, чтобы применять именно СОЗДАНИЕ классов вместо статических классов.

И вот я например, тоже не вижу разницы между сложным Singleton наворотом и простым Singleton::doAction(). Вообще, статические классы (со статическими переменными) чрезвыйчано удобны еще и тем, что они предоставляют как бы "глобальные" переменные для любой области видимости. И хэндлер для БД тому яркий пример.

Singleton (синглтон) — один из простейших для понимания шаблонов проектирования в PHP. Это обычный класс в PHP, в логику которого добавлена проверка на единственность создания его экземпляра.

Как создать синглтон:

  • хранить экземпляр класса в приватной статической переменной
  • инициализировать его через метод getInstance и сохранять в статической переменной, а если она была создана раньше, то вернуть ее
  • определить метод __construct в private и определить в нем логику создания instance
  • определить метод __clone (клонирование объекта) как private
  • определить метод __wakeup (вызывается перед unserialize) как private

Почему стоит использовать синглтон:

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

Когда стоит использовать Singleton:

  • подключение к БД
  • класс, инициализирующий настройки приложения, состояние, контекст

Почему не стоит использовать синглтон

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

Пример

Рассмотрим использование Singleton на примере создания соединения к базе данных MySQL.

Создадим класс DB. Информацию о соединении будем хранить в статической приватной переменной $_instance. Чтобы получить ее значение будем использовать статический метод getInstance(), в котором будем делать проверку на null переменной $_instance, в случае истины создавать ее через new self, иначе — возвращать ее.

Пример реализации этого класса:

_instance = new PDO("mysql:host=" . self::DB_HOST . ";dbname=" . self::DB_NAME, self::DB_USER, self::DB_PASS, ); } private function __clone () {} private function __wakeup () {} public static function getInstance() { if (self::$_instance != null) { return self::$_instance; } return new self; } }

Использование:

Сегодня я хочу разобрать шаблон проектирования "одиночка" , который очень часто используется в объектно-ориентированном программировании.

Шаблон проектирования "Одиночка" или Pattern Singleton нужен для того, чтобы у нас не было много однотипных объектов, а всегда использовался только один. В качестве примера можно привести класс для работы с базой данных.

Class DB {
protected $db;

Public function __construct() {
$this->
}

Public function get() {}
public function set() {}
public function del() {}
}

$db1 = new DB();
$db2 = new DB();

У нас уже 2 объекта $db1 и $db2 , а потом кто-нибудь, не зная, что уже есть такой объект, создаст третий и т.д. Это очень плохо сказывается на производительности и читаемости кода, а в нашем случае может произойти сбой, т.к. на хостинге ограниченное количество подключений к базе данных.

Чтобы решить эту проблему, и был придуман паттерн singleton .

Class DB {
protected $db;
static private $instance = null;

Private function __construct() {
$this->db = new Mysqli($host, $user, $pass, $database);
}

Private function __clone() {}

Static function getInstance() {
if(self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
}

$db = new DB(); // ошибка

$db = DB::getInstance();
$db2 = DB::getInstance();
$db3 = DB::getInstance();

Чтобы создать объект обычным способом было нельзя, мы делаем наш конструктор приватным , но также не забываем и про то, что объекты могут клонироваться и закрываем также метод __clone . Дальше мы создаём статическое свойство $instance , которое по умолчанию равно null . Теперь создаём статический метод getInstance() , который проверяет, равно ли наше статическое свойство null ? Если да, то мы создаём экземпляр нашего объекта и возвращаем его, а если же нет, то просто возвращаем его. Таким образом, у нас всегда будет один и тот же экземпляр, сколько бы мы их не создавали. Использовать его очень просто: присваиваем переменной значение, которое возвращает статический метод getInstance() , класса DB , а дальше работаем, как и с обычным объектом.