Page 1 of 1

OO 'Deep' registry..

Posted: Sun Mar 18, 2007 2:17 am
by Jenk
Ambiguous title, I apolgise. I couldn't think up anything more descriptive. I've not unit tested it, I've not I've got the effort to just yet. Will do at a later date. :)

EDIT: Added comments :)

Code: Select all

<?php
/**
 * Registry Manager
 * 
 * This class is the controller for the registry
 * @author Jenk
 */

class RegistryManager
{
	/**
	* The root object of the registry.
	* @var RegistryKey
	*/
	private $_keys;
	
	/**
	* The token for which to identify where the names of the string split
	* e.g. registry/key/anotherkey would be registry -> key -> anotherkey
	* @const string
	*/
	const SPLITTER = '/';
	
	/**
	* _split function; what converts the string to an array of keys.
	* @param string $key
	* @returns array
	*/
	private function _split ($key)
	{
		$key = trim ($key, self::SPLITTER);
		return explode(self::SPLITTER, $key);
	}
	
	/**
	* Lazy loader for the root registry object
	* @returns RegistryKey
	*/
	private function _getKeys()
	{
		if (!($this->_keys instanceof RegistryKey)) $this->_keys = new RegistryKey;
		
		return $this->_keys;
	}

	/**
	* Add key to the registry, and assign a value.
	* Assigns value blindly - i.e. previous values will be overwritten without warning.
	* @param string $name
	* @param mixed $value
	*/	
	public function addKey ($name, $value)
	{
		$parts = $this->_split($name);
		$pos = $this->_getKeys();
		
		foreach ($parts as $part)
		{
			if (!$pos->keyExists($part))
			{
				$stub = new RegistryKey();
				$pos->addKey($part, $stub);
				$pos =& $stub;
				unset($stub); // necessary for dereferencing.
			}
			else
			{
				$pos = $pos->getKey($part);
			}
		}
		
		$pos->value = $value;
	}

	/**
	* Check if $key exists
	* @param string $key
	* @returns bool 
	*/
	public function keyExists ($key)
	{
		$parts = $this->_split($key);
		$pos = $this->_getKeys();
		
		foreach ($parts as $part)
		{
			if (!$pos->keyExists($part))
			{
				return false;
			}
			else
			{
				$pos = $pos->getKey($part);
			}
		}
		
		return true;
	}
	
	/**
	* Retrieve RegistryKey $key
	* @throws Exception
	* @param string $key
	*/
	public function getKey ($key)
	{
		if (!$this->keyExists($key)) throw new Exception('Key does not exist.');
		
		$parts = $this->_split($key);
		$pos = $this->_getKeys();
		
		foreach ($parts as $part)
		{
			$pos = $pos->getKey($part);
		}
		
		return $pos;
	}
	
	/**
	* Convenience function, retrieves the value of $key, and not the key itself.
	* @returns mixed
	*/
	public function getValue ($key)
	{
		return $this->getKey($key)->value;
	}
}
		
/**
 * RegistryKey class, the registry key object which contains the value.
 *
 * @author Jenk
 */
class RegistryKey
{
	/**
	* The container for child RegistryKey objects
	* @var array
	*/
	private $_keys = array();
	
	/**
	* The container for the value of this key
	* @var mixed
	*/
	public $value = null;
	
	
	/**
	* Add a child key
	* @throws Exception
	* @param string $name
	* @param RegistryKey $key
	*/
	public function addKey ($name, RegistryKey $key)
	{
		$this->_keys[$name] = $key;
	}
	
	/**
	* Check if child key exists 
	* Note: this does not search the full tree, it will only check if $name is
	* an existing child key. If $key = "some/registry/branch" it will look for 
	* a child called exactly that and not a branch.
	* @param string $name
	* @returns bool
	*/
	public function keyExists ($name)
	{
		return isset($this->_keys[$name]);
	}
	
	/**
	* Returns the RegistryKey object of $name.
	* Note: This does not search the full tree. If $key = "some/registry/branch" 
	* it will look for a child called exactly that, not the branch.
	* @throws Exception
	* @param string $key
	* @returns RegistryKey 
	*/
	public function getKey ($key)
	{
		if (!$this->keyExists($key)) throw new Exception('Key does not exist.');
		else return $this->_keys[$key];
	}
	
	/**
	* Convenience function for returning the value of the RegistryKey $key, 
	* instead of the object.
	* @returns mixed
	*/
	public function getValue($key)
	{
		return $this->getKey($key)->value;
	}

        /**
        * Convenience function for adding new values
        * Does not search tree, but adds values with name as literal.
        * WARNING: Adding keys which contain the RegistryManager::SPLITTER will break search functionality!
        * 
        * @param string $name
        * @param mixed $value
        */
        public function addValue ($name, $value)
        {
                if (!$this->keyExists($name))
                {
                        $newKey = new RegistryKey;
                        $newKey->value = $value;
                        $this->addKey($name, $newKey);
                }
                else
                {
                    $newKey = $this->getKey($name);
                    $newKey->value = $value;
                }
        }
}

$reg = new RegistryManager;
$keyString = 'a/b/c';
$reg->addKey($keyString, 'value');
echo $reg->getValue($keyString); // 'value'
var_dump($reg->keyExists('a/b')); // 'bool(true)'
var_dump($reg->keyExists('a/b/d')); // 'bool(false)'

?>
Usage example:

Code: Select all

$reg = new RegistryManager;
$reg->addKey('Database/Password', 'pass');
$reg->addKey('Database/Username', 'user');
$reg->addKey('Database/Address', 'addy');

// later on..

$dbinfo = $reg->getKey('Database');
mysql_connect($dbinfo->getValue('Username'), $dbinfo->getValue('Password'), $dbinfo->getValue('Address'));
Small blog for it..

Inspired by Java's "Properties" file format, and of course the Windows registry. I didn't really 'need' this for anything, other than a challenge* and time filling (insomnia strikes,) though I forsee I will use it in the future as I like to organise my data, objects, and anything else I see fit for use in a registry. I am not 100% satisfied with the masses of loops.. I would prefer to have the string looped in one method only, but because even though the loops all do the same (i.e. move along the sections of the string) the inner bits of the loops do otherwise.. I might even make an iterator object for the string in future, but that's 'overkill' atm.

If anyone is nuts like me and wants to use this, please do - but please do include a link to where you found it.. "Jenk on http://www.devnetwork.net" is all that is required.

*I plan on O/RM'ing this.. wish me luck :lol:

Posted: Sun Mar 18, 2007 11:25 am
by John Cartwright
I've had a hard time looking at your code, comments would be helpful :)

Posted: Sun Mar 18, 2007 1:24 pm
by Christopher
It's not really a Registry. It is something like a Value Object that allows paths. The WACT DataSpace did this kind of thing long ago and it has been tried a number of different ways elsewhere. It is not a bad idea on its own, but it usually get abandoned. Not sure exactly why. I used something like it for a while but dropped it for simplicity. Take a look at the Zend_Config class for example.

Posted: Sun Mar 18, 2007 1:32 pm
by Chris Corbyn
Jcart wrote:I've had a hard time looking at your code, comments would be helpful :)
Agreed. I looked but the lack of comments left me without any desire to look closer ;) Comments dude? :)

Posted: Sun Mar 18, 2007 1:38 pm
by Jenk
Have added :)

Posted: Sun Mar 18, 2007 5:26 pm
by Ambush Commander
I agree with Arborint's assessment. It's like an array on steroids.

I would guess the reason why stuff like this gets abandoned is because it's a lot of overhead for not much benefit. The benefit is exceptions when you access undefined entries and the ability to make $array['foo'] distinct from the array that contains $array['foo']['bar']. In your usage example you don't take advantage of these two properties!

I've implemented something like this, albeit non-recursively (only one namespace!) In my case, the feature that justified the implementation was the ability to explicitly define the structure of the value object from multiple locations.

Posted: Sun Mar 18, 2007 8:53 pm
by Jenk
I disagree that it's not a registry - infact I think Registry is a very accurate description. I do agree it's an array on steroids - that was the aim, to bring more control over a multidimensional array, and to provide a single point of reference to a system that uses multiple registry objects.

You can make distinct separations. To expand on my example:

Code: Select all

$reg = new RegistryManager;
$reg->addKey('config/database', null); // perhaps store the DataObject here when you have instantiated it?

$dbKey = $reg->getKey('config/database');
$dbKey->addValue('username', 'user');
$dbKey->addValue('password', 'pass');
$dbKey->addValue('address', 'addy');

// all the above are now available directly from the manager ala:
$reg->getValue('config/database/username');

// that is distinct from perhaps:
$reg->getValue('app/pagename/title');

Posted: Wed Apr 04, 2007 12:09 am
by JeFFb68CAM
Dude that's a really awesome idea, I'm going to play with this more tomorrow.

If you could get this to like, write to an (encrypted?) file system I think it would be an awesome tool.

Posted: Wed Apr 25, 2007 5:14 pm
by Ollie Saunders
I would guess the reason why stuff like this gets abandoned is because it's a lot of overhead for not much benefit.
Yeah I agree with this. You could rewrite this to use the ArrayAccess interface and improve polymorphism.

One small vocab thing

Code: Select all

const SPLITTER = '/';
this is a delimiter or separator not so much a splitter. This is a splitter

Code: Select all

private function _split ($key)
but splitter is almost certainly better reserved for an object that splits things.