Шаблон проектирования „Registry“ на PHP

от автора

В этой статье хочу рассказать о своем методе реализации шаблона проектирования “Registry“ на PHP. Если кратко о шаблоне — это алгоритм, с помощью которого можно хранить переменные в одном месте. Про этот шаблон можно почитать, например, здесь. От себя же хочу добавить, что этот шаблон очень хорошо идет в связке с шаблоном “Facade“.

Реализация

Реализация может быть различой, я же сделал ее удобной для себя.

<?php 	/** 	 * Класс «Registry» хранит глобальные переменные и должен быть передан во все объекты. 	 * Класс защищен от повторного создания с помощью шаблона проектирования «Singleton». 	 */ 	 	final class Registry { 		private $vars = array(); // массив переменных 		 		 		 		 		// Singleton ---------------------------------------------------- 		static private $instance = NULL; // инстанс класса 		 		// создание и получение инстанса 		static function instance() { 			if (self::$instance == NULL) { 				self::$instance = new Registry(); 			}; 			 			return self::$instance; 		} 		 		// скрытие конструктора и клонирования 		private function __construct() {} 		private function __clone() {} 		// -------------------------------------------------------------- 		 		 		 		 		// публичные функции -------------------------------------------- 		// сохранение 		function set($name, $value = NULL, $type = 'simple') { 			if (!is_string($name)) { 				$this->errors('arg', 1, __METHOD__, 'string', gettype($name)); 				return false; 			}; 			 			// установка переменной 			switch ($type) { 				case 'protected': 					if (!isset($this->vars[$name]) || (isset($this->vars[$name]) && $this->vars[$name]['type'] == 'simple')) { 						$this->vars[$name] = array( 							'value' => $value, 							'type' => $type 						); 						return true; 					} else { 						$this->errors('var', 0, $name, __METHOD__); 						return false; 					}; 					 				case 'simple': 					if (isset($this->vars[$name]) && $this->vars[$name]['type'] == 'protected') { 						$this->errors('var', 0, $name, __METHOD__); 					} else { 						$this->vars[$name] = array( 							'value' => $value, 							'type' => $type 						); 						return true; 					}; 									 				default: 					$this->errors('var', 2, $name, __METHOD__); 					return false; 			}; 		} 		 		// получение 		function get($name) { 			if (!is_string($name)) { 				$this->errors('arg', 1, __METHOD__, 'string', gettype($name)); 				return false; 			}; 			 			if (isset($this->vars[$name])) { 				return $this->vars[$name]['value']; 			} else { 				$this->errors('var', 1, $name, __METHOD__); 				return false; 			} 		} 		// -------------------------------------------------------------- 		 		 		 		 		// ошибки (приватная функция) ----------------------------------- 		private function errors($type) { 			switch ($type) { 				case 'arg': 					// инициализация аргументов 					$number = func_get_arg(1); 					$method = func_get_arg(2); 					$must = func_get_arg(3); 					$given = func_get_arg(4); 					 					$call = debug_backtrace()[1]; 					trigger_error("argument <i>$number</i> passed to <i>$method()</i> must be an instance of <u>$must</u>, <u>$given</u> given, called in <b>" . $call['file'] . "</b> on line <b>" . $call['line'] . "</b> and defined", E_USER_WARNING); 					break; 					 				case 'var': 					// инициализация аргументов 					$error = func_get_arg(1); 					$name = func_get_arg(2); 					$method = func_get_arg(3); 					 					// ошибки 					$errors = array( 						array( 							'value' => "protected variable <i>$name</i> passed to <i>$method()</i> has already been registered", 							'type' => E_USER_ERROR 						), 						array( 							'value' => "variable <i>$name</i> passed to <i>$method()</i> has not been registered", 							'type' => E_USER_WARNING 						), 						array( 							'value' => "type of variable <i>$name</i> passed to <i>$method()</i> must be <u>simple</u> or <u>protected</u>", 							'type' => E_USER_WARNING 						) 					); 					 					$call = debug_backtrace()[1]; 					trigger_error($errors[$error]['value'] . ", called in <b>" . $call['file'] . "</b> on line <b>" . $call['line'] . "</b> and defined", $errors[$error]['type']); 					break; 			}; 		} 		// -------------------------------------------------------------- 	} ?>

“Singleton“

Во избежание копирования или создания нового инстанса, нужно применить шаблон “Singleton“. Это поможет избежать проблем с доступом к переменным.

// Singleton ---------------------------------------------------- 		static private $instance = NULL; // инстанс класса 		 		// создание и получение инстанса 		static function instance() { 			if (self::$instance == NULL) { 				self::$instance = new Registry(); 			}; 			 			return self::$instance; 		} 		 		// скрытие конструктора и клонирования 		private function __construct() {} 		private function __clone() {} 		// --------------------------------------------------------------

Для того, чтобы получить инстанс класса Registry, который защищен этим способом, нужно вызвать метод Registry::instance(), который возвращает объект, если он существует, в противном случае предварительно его создав. Создать объект снаружи класса через оператор new или клонировать невозможно. Т.к. этот метод статичен, его можно вызвать в любом месте кода и, как следствие, получить объект класса Registry.

Хранение значений

Класс позволяет хранить два типа переменных — защищенные (protected) и обычные (simple). Защищенные переменные невозможно изменить после создания, а обычные — возможно.

Установка значений

bool Registry::set(string $name, mixed $value [, string $type])

Где $name — имя переменной, $value — значение переменной, $type — необязательный тип переменной (по умолчанию 'simple'). Вернет true, в случае успешного добавления, и false, в случае ошибки. Если Вы попробуете переписать защищенную переменную, то будет выведена ошибка.

Получение значений

mixed Registry::get(string $name)

Где $name — имя переменной. Возвращает значение переменной $name или false, в случае ошибки. При попытке получить несуществующую переменную будет выведена ошибка.

Пример

// получение инстанса $registry = Registry::instance();  // установка защищенной переменной $registry->set('protected_var', 'This variable is protected', 'protected'); // установка обычной переменной $registry->set('simple_var', 'This variable is simple');  echo $registry->get('protected_var'); // => 'This variable is protected' echo $registry->get('simple_var'); // => 'This variable is simple' echo $registry->get('var'); // => false  // перезаписывание значения обычной переменной $registry->set('simple_var', 'Rewrite simple variable'); // перезаписывание значения защищенной переменной обернется ошибкой $registry->set('protected_var', 'Rewrite protected variable');
Ошибки

Класс имеет встроенный вывод ошибок, использующий функцию trigger_error(). Ниже приведены все возможные ошибки и их тип.

Тип Ошибка Значение
E_USER_ERROR protected variable test_var passed to Registry::set() has already been registered, called in /test.php on line 10 Нельзя изменять значение защищенной переменной test_var.
E_USER_WARNING variable test_var2 passed to Registry::get() has not been registered, called in /test2.php on line 5 Вы пытаетесь получить значение несуществующей переменной test_var2.
E_USER_WARNING type of variable test_var3 passed to Registry::set() must be simple or protected, called in /test3.php on line 7 Вы указали неверный тип переменной test_var3, он может быть либо simple, либо protected.
E_USER_WARNING argument 1 passed to Registry::set() must be an instance of string, array given, called in /test4.php on line 6 Первый аргумент метода Registry::set() должен быть строкой, Вы пытаетесь передать массив.

Заключение

Благодаря этому шаблону проектирования, в конструкторах классов, которые вызываются фасадом в моей системе, я могу получить и сохранить необходимые для работы объекты, которые я создавал ранее (например, шаблонизатор Smarty мне нужен практически везде). Существование защищенных переменных гарантирует мне, что я нигде не перепишу их значение и не положу из-за этого работу всего сайта, а защита объекта шаблоном Singleton гарантирует, что сохраненные переменные не потеряются. Мне кажется это очень удобным и хорошим способом хранить важные глобальные данные и получать их в любом месте кода.

ссылка на оригинал статьи http://habrahabr.ru/post/166359/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *