Jump to content

Recommended Posts

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.

Link to comment
https://forums.phpfreaks.com/topic/86050-config-system-advice/
Share on other sites

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.

Link to comment
https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439572
Share on other sites

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.

 

Link to comment
https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439616
Share on other sites

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.

Link to comment
https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439780
Share on other sites

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.

Link to comment
https://forums.phpfreaks.com/topic/86050-config-system-advice/#findComment-439854
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.