CORE

11 Июнь 2007
Эта статья является демонстрацией примера приведенного в статье Registry
У этой статьи есть продолжение Pattern: Registry

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


class CORE{

	private $tools;
	private static $instance;
	
	final private function __construct(){ 
		echo "CORE::__construct()\n";
	}
	
	static function & __instance(){
		if (!isset(self::$instance))
			self::$instance = new self;
		return self::$instance;
	}

	function __clone(){
		trigger_error('Clone is not allowed.', E_USER_ERROR);
	}
}

Теперь создать объект напрямую оператором new не получиться, а клонирование вызовет ошибку уровня E_USER_ERROR.

Свойство tools будет хранить в себе ссылки на все управляемые объекты, а instance ссылку на само ядро. Добавим методы регистрации и разнрегистрации объектов в ядре.


	static function & register($tool, $name=''){
		if (is_string($tool) && !$name)
			$name = $tool;
			
		if (self::has($name))
			throw new Exception("Try to re-register tool '$name'",1);
			
		$instance = self::__instance();
		$instance->tools[$name] = $instance->factory($tool);
		return $instance->tools[$name];	
	}
	
	static function unregister($name, $force=false){
		if (!self::has($name))
			throw new Exception("Try to unregister non-existing tool '$name'",1);
			
		$instance = self::__instance();
		unset($instance->tools[$name]);
		
		if ($force && is_callable(array($name,"kill"))) 
			call_user_func(array($name,"kill"));
	}

Здесь метод register принимает первым параметром объект или название класса из которого этот объект можно создать, а вторым имя под которым его нужно будет сохранить.

Метод unregister принимает название объекта который нужно уничтожить и флаг для форсирования уничтожения объекта.

Помимо этого используються еще два метода: has и factory, has проверяет не зарегистрирован ли уже объект с таким именем, а factory создает объект если указано только его название.


	static function has($name){
		$instance = self::__instance();
		
		if (isset($instance->tools[$name]))
			return true;
			
		return false;
	}
	
	static function & factory($name){
	
		if (is_object($name))
			return $name;

		if (!class_exists($name) && is_callable("__autoload"))
			__autoload($name);
		
		if (class_exists($name)){
			if(is_callable(array($name,"__instance")))
				return $instance->tools[$name] = call_user_func(array($name,"__instance")); // метод $name::__instance вызван статично
			return $instance->tools[$name] = new $name;
		}
		throw new Exception("Class '$name' doesn't declared and can't be loaded!");
	}

Немного подробнее о методе factory: он пытается создать экземпляр класса, когда известно только имя этого класса. Если такой класс не найден, то метод передает управление функции __autoload. Затем снова проверяет, доступен ли класс. Если не доступен, то бросает exception, а если доступен, то пытается создать его либо непосредственно используя оператор new, либо как паттерн Singleton используя метод __instance. При этот метод __instance вызывается статично!

Помимо всего этого нужен еще один метод, для доступа ко всему тому, что было зарегистрировано.


	static function & extract($name){
		if (!self::has($name))
			self::factory($name);
		$instance = self::__instance();
		return $instance->tools[$name];
	}

И заканчивает описание класса деструктор, который будет уничтожать объекты в обратном порядке их регистрации:


	function __destruct(){
		$instance = self::__instance();
		$tools = array_reverse(array_keys($instance->tools),true);
		foreach ($tools as $name){
			echo "Вызыв. деструктор $name\n";
			self::unregister($name,true);
		} 
		echo "Объект разрушен CORE\n";
	}

Теперь приведу пример работы с этим классом:


class B 
{
	private static $instance;
	
	final private function __construct(){ 
		echo "B::__construct()\n";
	}
	
	static function & __instance(){
		if (!isset(self::$instance))
			self::$instance = new self;
		return self::$instance;
	}
	
	public function kill(){
		self::$instance = null;
	}
	
	public function __destruct(){
		echo "Объект разрушен   B\n";
	}
}

class C 
{
	public $B;
	public function __construct($B){
		echo "C::__construct()\n";
		$this->setB($B);
	}
	public function setB($B){
		$this->B = $B;
	}
	public function __destruct(){
		echo "Объект разрушен   C\n";
	}
}


CORE::__instance();
CORE::register('B');
CORE::register(new C(CORE::extract('B')),'C');


/** RESULT ********
CORE::__construct()
B::__construct()
C::__construct()
Вызыв. деструктор C
Объект разрушен   C
Вызыв. деструктор B
Объект разрушен   B
Объект разрушен CORE
********************/
Комментирование отключено.