<?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;
}