OO 'Deep' registry..

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

OO 'Deep' registry..

Post 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:
Last edited by Jenk on Wed Apr 04, 2007 5:25 am, edited 3 times in total.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

I've had a hard time looking at your code, comments would be helpful :)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post 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.
(#10850)
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post 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? :)
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Have added :)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post 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.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post 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');
Last edited by Jenk on Wed Apr 04, 2007 5:27 am, edited 1 time in total.
JeFFb68CAM
Forum Newbie
Posts: 15
Joined: Tue Apr 03, 2007 11:17 pm

Post 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.
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post 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.
Post Reply