Akrabat_Config (2nd Attempt)
Update! This version has been superceded! Check out Akrabat_Config (Take Three!) for an even better version…
Nico Edtinger was kind enough to review Akrabat_Config and pointed out that it wouldn’t handle a key with multiple dots in it. His post to fw-general:
One method I don’t “like” is processSection(). If I have a name like “foo.bar.baz” it would be saved as $config[‘foo’][‘bar’], dropping the last part because you explode without a third parameter. It should be
$pieces = explode(‘.’, $key, 2);I’d also change the check for a dot to
if(strpos($key, ‘.’)) {That would work with a key with a leading dot (being stored just as is instead of $config[”][…]) and the explode doesn’t need to parse a string that doesn’t have a dot anyway and create an array that’s not needed.
Thus, this is my second attempt at Akrabat_Config:
The new tests are:
< ?php
require_once 'PHPUnit2/Framework/TestCase.php';
include "Zend.php";
class ConfigTest extends PHPUnit2_Framework_TestCase
{
protected $iniFilename;
function setUp()
{
$this->iniFilename = dirname(__FILE__).'/data/config.ini';
}
function testLoadAll()
{
Zend::loadClass('Akrabat_Config');
$config = new Akrabat_Config($this->iniFilename, 'all');
$this->assertEquals('all', $config->hostname);
$this->assertEquals('all', $config->test['me']);
}
function testInclude()
{
Zend::loadClass('Akrabat_Config');
$config = new Akrabat_Config($this->iniFilename, 'staging');
$this->assertEquals('staging', $config->hostname);
$this->assertEquals('staging', $config->test['me']);
}
function testMultiLevels()
{
Zend::loadClass('Akrabat_Config');
$config = new Akrabat_Config($this->iniFilename, 'multi');
zend::dump($config);
$this->assertEquals('four', $config->one['two.three']);
$this->assertEquals('five', $config->one['two.three.four']);
}
function testLeadingDot()
{
Zend::loadClass('Akrabat_Config');
$config = new Akrabat_Config($this->iniFilename, '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"));
}
}
?>
with data/config.ini:
[all] hostname = all test.me = all [staging] include = all hostname = staging test.me = staging [multi] one.two.three = four one.two.three.four = five one.two..three = dotdotthree [dot] . = dot .. = doubledot t. = t-dot .t = dot-t
and Akrabat_Config looks like this:
< ?php
class Akrabat_Config
{
private $_config;
/**
* Load the section $section from the ini file called $filename.
* If any keys with $section are called "include", then the section
* pointed to by the "include" is then included first. Thus, the keys
* in $section will override any keys of the same name in the sections
* that have been "include"ed.
*
* example ini file:
* [all]
* db.connection = database
* hostname = live
*
* [staging]
* include = all
* hostname = staging
*
* after callgin $config = new Akrabat_Config($file, 'staging'); then
* $config->hostname = staging
* $config->db['connection'] = database
*
*
* @param string $filename
* @param string $section
*/
function __construct($filename, $section)
{
$iniArray = parse_ini_file($filename, true);
$config = array();
if(isset($iniArray[$section]))
{
foreach($iniArray[$section] as $key => $value)
{
if($key == 'include')
{
if(isset($iniArray[$value]))
{
$config = array_merge($config, $this->processSection($iniArray[$value]));
}
unset($iniArray[$section][$key]);
}
}
$config = array_merge($config, $this->processSection($iniArray[$section]));
}
else
{
throw new Exception("No section '$section' in $filename'");
}
$this->_config = $config;
}
/**
* Helper function to handle single level namespace in the key
*
* @param array $section
* @return array
*/
protected function processSection($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);
}
}
?>
Looks interesting. I've always hated to have my settings all over the place. It's also something that I don't like in my bootstap file. It's clutter ;) Then again, there ini functions in PHP if i'm not mistaken, so this could be considered reinventing the wheel…
Yes – this class is a thin wrapper around the ini handling function. Though it provides additional functionality by allowing for overriding of keys in one section with keys in another.
On the fw-general list we are talking about allowing for loading multiple files into one config object. I quite like that idea so might play with it tonight.