Location: PHPKode > scripts > Registry and Service Locator > registry-and-service-locator/Registry.class.php
<?php
/**
 * Registry/Service Locator class
 * 
 * # FEATURES
 * 	- Follows singleton/registry design pattern
 * 	- Implements ArrayAccess/Iterator SPL interfaces
 * 	- Can act as a service locator (bind abstract objects to concrete objects)
 * 	- Also supports magic methods (__get, __set, __isset, etc.)
 * 	- Option for allowing overwrite of registry objects
 * # CHANGELOG
 * 	- 1.0 (Feb 8, 2009) - Initial release
 * # TODO LIST
 * 	- Make service locator work more seperately from registry
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @author SoBeNoFear <hide@address.com>
 * @copyright 2007-2009 Ian Unruh
 * @license LGPLv3 http://gnu.org/licenses/lgpl.txt
 * @version 1.0
 * @package RSL
 */
class Registry implements ArrayAccess, Iterator {
	/**
	 * Singleton instance of registry
	 * @var object
	 */
	protected static $__instance = null;
	
	/**
	 * Array of objects and abstract bindings
	 * @var array
	 */
	protected static $__objects = array ();
	
	/**
	 * Setting for the ability to overwrite a registry object
	 * @var bool
	 */
	protected static $__overwrite = false;
	
	/**#@+
	 * Protected magic methods
	 * 
	 * These magic methods are protected to prevent cloning,
	 * serializing, and constructing the registry, which would
	 * break the singleton pattern
	 * 
	 * @return void
	 */
	protected function __construct() {
	}
	public function __clone() {
	    throw new Registry_Exception("Because of the nature of a singleton, method is disallowed: ".__METHOD__, Registry_Exception::SingletonRestriction);
	}
	public function __sleep() {
	    throw new Registry_Exception("Because of the nature of a singleton, method is disallowed: ".__METHOD__, Registry_Exception::SingletonRestriction);
	}
	public function __wakeUp() {
	    throw new Registry_Exception("Because of the nature of a singleton, method is disallowed: ".__METHOD__, Registry_Exception::SingletonRestriction);
	}
	
	/**
	 * Retrieve or construct instance of the registry
	 * @return object
	 */
	public static function singleton() {
		if (! is_object ( self::$__instance )) {
			$me = __CLASS__;
			self::$__instance = new $me ( );
		}
		return self::$__instance;
	}
	
	/**
	 * @see Registry::singleton()
	 */
	public static function instance() {
		return self::singleton ();
	}
	
	/**
	 * Retrieve pre-existing object or create bound class
	 * 
	 * This works not only as a Registry retriever, but as
	 * a Service Locator; if a concrete class is bound to
	 * an abstract name, it is created when called
	 *
	 * @param string $alias Object alias or abstract to retrieve
	 * @param unknown_type $args [optional] Parameters to construct concrete class with
	 * @return object|bool Object on success; false on failure
	 */
	public static function getObj($alias, $args = array()) {
		if (array_key_exists ( $alias, self::$__objects )) {
			if (is_string ( self::$__objects[$alias] )) {
				$object = new ReflectionClass ( self::$__objects [$alias] );
				if (count ( $args ))
					return $object->newInstanceArgs ( $args );
				return $object->newInstance ();
			}
			return self::$__objects [$alias];
		}
		return false;
	}
	
	/**
	 * Set object to registry alias or bind concrete to abstract
	 * 
	 * Like getObj, this also can function like a service locator,
	 * just use a string for the name of the class you want to bind
	 * to an abstract name of a class
	 * 
	 * It also handles assocative arrays of objects/concretes, like in this example:
	 * <code>
	 * 	$objects['Request'] = 'Request';
	 * 	$objects['Router'] = $router;
	 * 	$objects['Db'] = new Db();
	 * 
	 * 	$registry = Registry::singleton();
	 * 	$registry->setObj($objects);
	 * 	// OR
	 * 	Registry::setObj($objects);
	 * </code>
	 *
	 * @param string|array $alias Abstract to bind/alias to set/array of assoc objects
	 * @param string|object $object [optional] Concrete/object to set
	 * @return void
	 */
	public static function setObj($alias, $object = '') {
		if (is_array ( $alias )) {
			foreach ( $alias as $a => $o )
				self::setObj ( $a, $o );
			return;
		}
		if (array_key_exists ( $alias, self::$__objects ) && ! self::$__overwrite)
			throw new Registry_Exception ( "Overwriting not allowed on object: {$alias}", Registry_Exception::OverwriteNotAllowed );
		self::$__objects [$alias] = $object;
	}
	
	/**
	 * Check for existing object/bound abstract
	 *
	 * @param string $alias Object/Abstract to check
	 * @return bool Whether it exists or not
	 */
	public function isObj($alias) {
		return array_key_exists ( $alias, self::$__objects );
	}
	
	/**
	 * Unset object/unbind abstract
	 *
	 * @param string $alias Abstact/object to unset
	 * @return bool Whether the object existed or not to begin with
	 */
	public function unsetObj($alias) {
		if (array_key_exists ( $alias, self::$__objects )) {
			unset ( self::$__objects [$alias] );
			return true;
		}
		return false;
	}
	
	/**
	 * @see Registry::getObj()
	 */
	public function __get($alias) {
		return self::getObj ( $alias );
	}
	
	/**
	 * @see Registry::setObj()
	 */
	public function __set($alias, $object) {
		return self::setObj ( $alias, $object );
	}
	
	/**
	 * @see Registry::isObj()
	 */
	public function __isset($alias) {
		return self::isObj ( $alias );
	}
	
	/**
	 * @see Registry::unsetObj()
	 */
	public function __unset($alias) {
		return self::unsetObj ( $alias );
	}
	
	/**
	 * @see Registry::getObj()
	 */
	public function offsetGet($alias) {
		return self::getObj ( $alias );
	}
	
	/**
	 * @see Registry::setObj()
	 */
	public function offsetSet($alias, $object) {
		return self::setObj ( $alias, $object );
	}
	
	/**
	 * @see Registry::isObj()
	 */
	public function offsetExists($alias) {
		return self::isObj ( $alias );
	}
	
	/**
	 * @see Registry::unsetObj()
	 */
	public function offsetUnset($alias) {
		return self::unsetObj ( $alias );
	}
	
	/**
	 * Service locator-like alias for setObj
	 *
	 * @see Registry::setObj()
	 * @param string $abstract
	 * @param string $concrete
	 * @return void
	 */
	public static function bind($abstract, $concrete) {
		if (self::isObj ( $abstract ) && ! self::$__overwrite) {
			throw new Registry_Exception ( "Can't rebind bound abstract: {$abstract}", Registry_Exception::RebindNotAllowed );
		}
		self::setObj ( $abstract, $concrete );
	}
	
	/**
	 * Service Locator-like alias for unsetObj
	 * @see Registry::unsetObj()
	 */
	public static function unbind($abstract) {
		return self::unsetObj ( $abstract );
	}
	
	/**
	 * Service Locator-like alias for isObj
	 * @see Registry::isObj()
	 */
	public static function bound($abstract) {
		return self::isObj ( $abstract );
	}
	
	/**
	 * Service Locator-like alias for getObj
	 * @see Registry::getObj()
	 */
	public static function getConcrete($abstract, $args = array()) {
		return self::getObj ( $abstract, $args );
	}
	
	/**#@+
	 * Methods that allow cycling through the objects array
	 * 
	 * Declared to satisfy the Iterator interface
	 * Allows for procedures like this:
	 * <code>
	 * 	foreach(Registry::singleton() as $a=>$o){
	 * 		## do something with this registry object
	 *	}
	 * </code>
	 */
	public function current() {
		return current ( self::$__objects );
	}
	
	public function key() {
		return key ( self::$__objects );
	}
	
	public function next() {
		return next ( self::$__objects );
	}
	
	public function rewind() {
		return reset ( self::$__objects );
	}
	
	public function valid() {
		return ( bool ) $this->current ();
	}
	
	/**
	 * Retrieve the overwrite ability
	 * @return bool
	 */
	public static function getOverwrite() {
		return self::$__overwrite;
	}
	
	/**
	 * Set the overwrite ability
	 * 
	 * @param bool $overwrite
	 * @return bool If set was successful
	 */
	public static function setOverwrite($overwrite) {
		if (is_bool ( $overwrite )) {
			self::$__overwrite = $overwrite;
			return true;
		}
		return false;
	}
}

/**
 * Exception codes relevent to the Registry class
 * @author SoBeNoFear <hide@address.com>
 * @copyright 2007-2009 Ian Unruh
 */
class Registry_Exception extends Exception {
	const OverwriteNotAllowed = 0;
	const RebindNotAllowed = 1;
	const SingletonRestriction = 2;
}
Return current item: Registry and Service Locator