trq Posted January 15, 2008 Share Posted January 15, 2008 I'm trying to roll my own simple framework and have pretty much decided to start at the config system. I'm semi happy with the approuch I'm taking but there are a few things I dislike and would like to get some advice on. The way I have developed the system means that I cannot make Proem_Config a singleton, though ideally I would only want one (master) config object. The reason for this is the fact that Proem_Config itself can contain other instances of Proem_Config. Maybe this is the wrong approuch. I guess one way would be to make Proem_Config a singleton and have it contain some other type when needed. Another thing that I am a little concerned about is the inablity to lock entries as read-only. I've tried several different implementations trying to get this effect, but none of them have been very clean at all. Anyway, I'm just going to post the code (I'll post enough to get it working in case someone wants to test it), kinda looking for some pointers / ideas / thoughts. I'm not much chop at OOP either, so any advice there would be most welcomed as well. Sorry, its not reall well commented at the moment (at all), but hopefully you'll get the idea. /usr/share/php/proem/boot/init.php <?php define('PROEM_DIR', realpath(dirname(__FILE__) . '/..')); define('PROEM_SRC_DIR', PROEM_DIR . '/src'); define('PROEM_LIB_DIR', PROEM_DIR . '/lib'); define('PROEM_CONF_DIR',PROEM_DIR . '/etc'); function autoloader($class) { $path = str_replace('_','/',$class); foreach (array(PROEM_SRC_DIR, PROEM_LIB_DIR) as $dir) { if (file_exists($dir . '/' . $path . '.php')) { require_once $dir . '/' . $path . '.php'; } elseif (is_dir($path)) { $lastpart = basename($path); if (file_exists($path . '/' . $lastpart . '.php')) { require_once $path . '/' . $lastpart . '.php'; } } } } spl_autoload_register('autoloader'); $conf = Proem_Config_Loader::loadFile(PROEM_CONF_DIR . '/config.php'); $conf->db = array( 'name' => 'test', 'user' => 'thorpe', 'pass' => '*****' ); Proem_Config_Loader::loadFile(PROEM_CONF_DIR . '/test.php',$conf); print_r($conf); ?> /usr/share/php/proem/src/Proem/Config.php <?php class Proem_Config { protected $_data; public function __construct($array) { $this->_data = array(); foreach ($array as $key => $val) { if (is_array($val)) { $this->_data[$key] = new Proem_Config($val); } else { $this->_data[$key] = $val; } } } public function __get($key) { if (array_key_exists($key, $this->_data)) { return $this->_data[$key]; } return false; } public function __set($key,$val) { if (is_array($val)) { $this->_data[$key] = new Proem_Config($val); } else { $this->_data[$key] = $val; } } public function __isset($key) { return isset($this->_data[$key]); } public function __unset($key) { unset($this->_data[$key]); } } /usr/share/php/proem/src/Proem/Config/Loader.php <?php class Proem_Config_Loader { public static function loadFile($file, Proem_Config $conf = null) { $arr = explode('.', $file); $ext = $arr[count($arr)-1]; $loadername = 'Proem_Config_' . ucfirst(strtolower($ext)); $loader = new $loadername; $arr = $loader->load($file); if (is_null($conf)) { return new Proem_Config($arr); } else { foreach ($arr as $key => $val) { $conf->$key = $val; } return $conf; } } } /usr/share/php/proem/src/Proem/Config/TypesInterface.php <?php interface Proem_Config_TypesInterface { public function load($file); } /usr/share/php/proem/src/Proem/Config/Php.php <?php class Proem_Config_Php implements Proem_Config_TypesInterface { public function load($file) { if (file_exists($file)) { include $file; return $config; } return false; } } /usr/share/php/proem/etc/config.php <?php $config = array('foo' => 'boo','bazz' => 'bob'); /usr/share/php/proem/etc/test.php <?php $config = array('foo' => 'overiden', 'you' => 'twit'); ?> This is by no means the finnished product, just something a hacked out this morning trying to get ideas. Any advice / ideas much appreciated. Quote Link to comment https://forums.phpfreaks.com/topic/86050-config-system-advice/ Share on other sites More sharing options...
448191 Posted January 15, 2008 Share Posted January 15, 2008 I'm not a 100% sure why you need a composite structure, but here's something that works: <?php class Proem_Config_Exception extends Exception {} class Proem_Config_Exception_ItemLocked extends Proem_Config_Exception {} class Proem_Config { private $data = array(); private $locked = array(); private static $created = false; public static function factory(Array $array){ if(self::$created){ throw new Proem_Config_Exception('Object already exists.'); } self::$created = true; return new self($array); } private function __construct(Array $array) { foreach($array as $key => $value) { $this->set($key, $value); } } public function get($key) { if(isset($this->data[$key])) { return $this->data[$key]; } } public function lock(Array $keys){ $this->locked = array_flip($keys); } public function unlock(Array $keys){ $this->locked = array_flip(array_diff(array_flip($this->locked), $keys)); } public function set($key, $value){ if(isset($this->locked[$key])){ throw new Proem_Config_Exception_ItemLocked('Item has been locked.'); } if(is_array($value)){ $this->data[$key] = new Proem_Config($value); } else { $this->data[$key] = $value; } } public function add($key, $value){ if($this->exists($key)){ throw new Proem_Config_Exception('Key already exists, use Proem_Config::set() for overwriting.'); } if(is_array($value)){ $this->data[$key] = new Proem_Config($value); } else { $this->data[$key] = $value; } } public function exists($key){ return isset($this->data[$key]); } public function scan($key, Proem_Config $register = null){ if($register === null){ $register = $this; } if(!$this->exists($key)){ foreach($this->data as $item){ if($item instanceof Proem_Config){ if(null !== ($target = $item->scan($key, $item))){ return $target; } } } } else { return $this->get($key); } } public function remove($key){ if(isset($this->locked[$key])){ unset($this->locked[$key]); } unset($this->data[$key]); } } $proemcfg = Proem_Config::factory(array('foo' => 'boo','bazz' => 'bob')); $proemcfg->set('more', array('foo' => 'bar', 'you' => 'twit')); try { $proemcfg->add('foo', array('blah' => 'bar', 'you' => 'twit')); } catch(Proem_Config_Exception $e){ echo $e->getMessage().PHP_EOL; $proemcfg->remove('foo'); $proemcfg->add('foo', array('blah' => 'bar', 'bar' => 'twit')); $proemcfg->lock(array('foo', 'bazz')); } try { $proemcfg->set('foo', array('blah' => 'bar', 'you' => 'twit')); } catch(Proem_Config_Exception_ItemLocked $e){ echo $e->getMessage().PHP_EOL; $proemcfg->unlock(array('foo')); $proemcfg->set('foo', array('blah' => 'bar', 'meh' => 'twit')); $foo = $proemcfg->get('foo'); $foo->set('doh', array('uch' => 1, 'flah' => 2)); } var_dump($proemcfg->exists('meh')); var_dump($proemcfg->exists('foo')); var_dump($proemcfg->get('foo')->exists('meh')); var_dump($proemcfg->scan('meh')); var_dump($proemcfg->scan('flah')); var_dump($proemcfg); $proemcfg2 = Proem_Config::factory(array('foo' => 'boo','bazz' => 'bob')); ?> If you have more metadata than just a 'locked' flag, better to wrap your data in a meta object, rather than use a keyed array such as in the example above. Quote Link to comment https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439572 Share on other sites More sharing options...
448191 Posted January 15, 2008 Share Posted January 15, 2008 Two other things: 1) If you want to use the magic methods, don't let them operate on the data directly. Instead delegate to the regular methods: public function __get($key){ return $this->get($key); } 2) To create an object from a file, do one of two things: a) Keep the source completely separated from the object by simply passing the resulting data: $parser = new DOMFacade($filePath); $proemcfg = Proem_Config::factory($parser->getArray()); b) Define a type that config factory accepts: $parser = new Proem_Config_Xml($filePath); $proemcfg2 = Proem_Config::factory($parser); The later is only useful if the config object needs more of the parser's interface, for example if it needs to be able to make persistent changes. Quote Link to comment https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439616 Share on other sites More sharing options...
trq Posted January 15, 2008 Author Share Posted January 15, 2008 Thanks heaps for your time 448191, I'm going to take a good look at this when I get home from work, see how it all fits together. I'm not a 100% sure why you need a composite structure I'm wasn't even aware Id'e made one, will be looking that up too. feeling like a bit of a newb to OOP. Quote Link to comment https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439780 Share on other sites More sharing options...
448191 Posted January 15, 2008 Share Posted January 15, 2008 Thanks heaps for your time 448191, I'm going to take a good look at this when I get home from work, see how it all fits together. Don't mention it. I'm not a 100% sure why you need a composite structure I'm wasn't even aware Id'e made one, will be looking that up too. feeling like a bit of a newb to OOP. On second thought, it's rather a good idea. It comes natural with hierarchical source data. Though what I wrote behaves more like a multidimensional array than an XML document. Quote Link to comment https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439854 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.