Developing software in the Real World

Akrabat_Config (4th Go!)

Ok, I’m dense!

I’ve finally worked out what Nico was saying in that Akrabat_Config doesn’t allow for more than one included section when using the “include=” construct. This is because parse_ini_file() will overwrite keys of the same name.

Thus to support multiple sections the syntax has to be:

include = one,two,three

Akrabat_Config now looks like this:


hostname = staging
* $config->db['connection'] = database
*
*
* @param string $filename
* @param string $section
*/
function __construct($filename=null, $section=null)
{
$this->_config = array();
if (!is_null($filename)) {
$this->load($filename, $section);
}
}

/**
* Load an inifile, overwriting any duplicate keys.
* If $section is null, then the entire file is loaded.
*
* @param string $filename
* @param string $section
*/
public function load($filename, $section=null)
{
$iniArray = parse_ini_file($filename, true);
if ($section) {
if( isset($iniArray[$section])) {
$this->_config = array_merge($this->_config, $this->processIncludes($iniArray, $section));
} else {
throw new Exception("No section '$section' in $filename'");
}
}
else {
foreach($iniArray as $section=>$value) {
if (is_array($value)) {
if(!isset($this->_config[$section])) {
$this->_config[$section] = array();
}
$this->_config[$section] = array_merge($this->_config[$section], $this->processIncludes($iniArray, $section));
} else {
$this->_config[$section] = $value;
}
}
}
}

/**
* Helper function to process the "include=" inheritance and then
* process the "dot" single level sub-array syntax in each key.
*
* @param array $iniArray
* @param string $section
* @return array
*/
protected function processIncludes($iniArray, $section)
{
$config = array();
foreach ($iniArray[$section] as $key => $value) {
if ($key == 'include') {
$sections = explode(',', $value);
foreach ($sections as $s) {
if( isset($iniArray[$s])) {
$config = array_merge($config, $this->processLevelsInSection($iniArray[$s]));
}
}
unset($iniArray[$section][$key]);
}
}

$config = array_merge($config, $this->processLevelsInSection($iniArray[$section]));
return $config;
}

/**
* Helper function to handle single level namespace in the key
*
* @param array $section
* @return array
*/
protected function processLevelsInSection($section)
{
$config = array();
foreach ($section as $key=>$value) {
if (strpos($key, '.')) {
$pieces = explode('.', $key, 2);
if (!empty($pieces[1])) {
$config[$pieces[0]][$pieces[1]] = $value;
} else {
$config[$key] = $value;
}
} else {
$config[$key] = $value;
}
}
return $config;
}

/**
* @param string $name
* @param mixed $default
* @return mixed
*/
function get($name, $default=false)
{
$result = $default;
if (isset($this->_config[$name])) {
$result = $this->_config[$name];
}
return $result;
}

/**
* magic function so that $config->value will work.
*
* @param string $name
* @return mixed
*/
function __get($name)
{
return $this->get($name);
}

/**
* This is a read only class...
*
* @param string $name
* @param mixed $value
*/
function __set($name, $value)
{
throw new Exception('Akrabat_Config is read only!');
}

}
?>

Tests:

_iniFileConfig = dirname(__FILE__).'/data/config.ini';
$this->_iniFileOne = dirname(__FILE__).'/data/one.ini';
$this->_iniFileTwo = dirname(__FILE__).'/data/two.ini';
}

function tearDown()
{
}

function testLoadSectionAll()
{
$config = new Akrabat_Config($this->_iniFileConfig, 'all');
$this->assertEquals('all', $config->hostname);
$this->assertEquals('all', $config->test['me']);
}

function testSectionInclude()
{
$config = new Akrabat_Config($this->_iniFileConfig, 'staging');
$this->assertEquals('staging', $config->hostname);
$this->assertEquals('staging', $config->test['me']);
$this->assertEquals('2', $config->secondVar);
}

function testSectionMultiLevels()
{
$config = new Akrabat_Config($this->_iniFileConfig, 'multi');

$this->assertEquals('four', $config->one['two.three']);
$this->assertEquals('five', $config->one['two.three.four']);
$this->assertEquals('dotdotthree',$config->one['two..three']);
}

function testSectionLeadingDot()
{
$config = new Akrabat_Config($this->_iniFileConfig, 'dot');
$this->assertEquals('dot', $config->get("."));
$this->assertEquals('doubledot', $config->get(".."));
$this->assertEquals('t-dot', $config->get("t."));
$this->assertEquals('dot-t', $config->get(".t"));
}

function testLoadEntireConfigFile()
{
$config = new Akrabat_Config($this->_iniFileConfig);
$this->assertEquals('all', $config->all['hostname']);
$this->assertEquals('all', $config->all['test']['me']);

// ensure that the include=all in "staging" works
$this->assertEquals('avalue', $config->staging['akey']);

// test multi-level
$multi = $config->get('multi');
$this->assertEquals('four', $multi['one']['two.three']);

// test top level
$this->assertEquals('1', $config->toplevel);
}

function testReadOnly()
{
$config = new Akrabat_Config($this->_iniFileConfig);
try {
$config->test = '32';
}
catch (Exception $expected) {
return;
}

$this->fail('An expected Exception has not been raised.');
}

function testSecondFileOverwritesFirst()
{
$config = new Akrabat_Config();
$config->load($this->_iniFileOne);
$this->assertEquals('one', $config->akey);

$config->load($this->_iniFileTwo);
$this->assertEquals('two', $config->akey);

$this->assertEquals('two', $config->db['hostname']);
$this->assertEquals('thisDb', $config->db['database']);
}

}

?>

Ini files required for testing:
config.ini:


one.ini:


two.ini:

Akrabat_Config is turning into a good learning exercise! Especially as it’s conceptually very very simple…

7 thoughts on “Akrabat_Config (4th Go!)

  1. Hey Rob,

    The following code should fix your quotes problem in WordPress:

    [code]
    add_filter('the_content', 'strip_smart_quotes');

    function strip_smart_quotes($data)
    {
    $data = str_replace(array("'", "'"), "'",$data);
    $data = str_replace(array(""", """), '"', $data);
    $data = str_replace("″", '"', $data);
    $data = str_replace("′", "'", $data);
    return $data;
    }
    [/code]

    This is a direct copy from my Wisual.Syntax plugin so should work fine for you.

    BTW – thanks for your work on the config class, it is coming along nicely…

    Matthew

  2. Hi Matthew!

    I'm using a modified version of your Visual.Syntax plugin already. Seems that I commented out the line:

    add_filter('the_content', 'strip_smart_quotes');

    whilst testing and forgot to reinstate it…

    Excellent work btw!

  3. Doesn't seem to work completely though :(

    Look at the 32 in the test function testReadOnly()

    $config->test = '32?;

    Nearly sure that the second quote is not a '

Thoughts? Leave a reply

Your email address will not be published. Required fields are marked *