Skip to content

Instantly share code, notes, and snippets.

@iwyg
Last active December 23, 2015 05:09
Show Gist options
  • Save iwyg/6585278 to your computer and use it in GitHub Desktop.
Save iwyg/6585278 to your computer and use it in GitHub Desktop.
dot notation aware config class
<?php
/**
* This File is part of the Acme\Configuration package
*
* (c) Thomas Appel <[email protected]>
*
* For full copyright and license information, please refer to the LICENSE file
* that was distributed with this package.
*/
namespace Acme\Configuration;
/**
* @class Config
* @package Acme\Configuration
* @version $Id$
*/
class Config
{
/**
* config
*
* @var array
*/
private $config;
/**
* reference pointer to config values
*
* @var array
*/
private $reference;
/**
* @param array $config
*
* @access public
*/
public function __construct(array $config = [])
{
$this->reference = [];
$this->config = $config;
}
/**
* get a config item
*
* @param mixed $key
* @param mixed $default
*
* @access public
* @return mixed
*/
public function get($key, $default = null)
{
if ($result = $this->doGet($this->config, $key, '.', $key, explode('.', $key))) {
return $result;
}
return $default;
}
/**
* set a congig value
*
* @param mixed $param
* @param mixed $value
*
* @access public
* @return void
*/
public function set($key, $value)
{
return $this->configSet($key, $value, $this->config);
}
/**
* configSet
*
* @param string $query
* @param mixed $value
* @param array $input
* @param string $separator
*
* @access private
* @return mixed
*/
private function configSet($query, $value, array &$input = [], $separator = '.')
{
$keys = explode($separator, $query);
$pointer = &$input;
while (count($keys) > 0) {
$pointer = is_array($pointer) ? $pointer : [];
$pointer[$key = array_shift($keys)] = isset($pointer[$key]) ? $pointer[$key] : [];
$pointer = &$pointer[$key];
}
$pointer = $value;
$this->reference[$query] = $value;
}
/**
* doGet
*
* @param mixed $array
* @param mixed $query
* @param string $separator
* @param mixed $originalQ
* @param array $keys
*
* @access protected
* @return mixed
*/
protected function doGet(&$array = [], $query = null, $separator = '.', $originalQ = null, array $keys = [])
{
// if the query is already parsed return immediately
if ($result = $this->getReference($originalQ)) {
return $result;
}
return $this->configGet($array, $query, $separator, $originalQ, $keys, $result);
}
/**
* configGet
*
* @param mixed $array
* @param string $query
* @param string $separator
* @param string $originalQ
*
* @access private
* @return mixed
*/
private function configGet(&$array = [], $query = null, $separator = '.', $originalQ = null, array $keys = [], $result = null)
{
$key = array_shift($keys);
// we set both queries as pointer to the input array for faster
// retrieval
if (isset($array[$key])) {
if (is_array($result = $array[$key]) and ($count = count($keys)) > 0) {
if (1 === $count) {
$this->reference[substr($originalQ, -strrpos($originalQ, $separator))] =& $array[$key];
}
return $this->doGet($array[$key], implode($separator, $keys), $separator, $originalQ, $keys);
} else {
$this->reference[$originalQ] =& $array[$key];
return $this->reference[$originalQ];
}
}
return $result;
}
/**
* getReference
*
* @param mixed $query
*
* @access private
* @return mixed
*/
private function getReference($query)
{
if (isset($this->reference[$query])) {
return $this->reference[$query];
}
return false;
}
}
<?php
/**
* This File is part of the Acme\Configuration\Tests package
*
* (c) Thomas Appel <[email protected]>
*
* For full copyright and license information, please refer to the LICENSE file
* that was distributed with this package.
*/
namespace Acme\Configuration\Tests;
use Acme\Configuration\Config;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function testConfigGet()
{
$config = new Config($data = $this->getConfigData());
$this->assertEquals('baz', $config->get('foo.bar'));
$this->assertEquals(['bar' => 'baz'], $config->get('foo'));
$this->assertEquals('some value', $config->get('zoom.somestuff'));
$this->assertEquals(['somestuff' => 'some value'], $config->get('zoom'));
$this->assertEquals('nested', $config->get('deep.deep.deep.deep.deep'));
}
/**
* @test
*/
public function testConfigGetShouldReturnNull()
{
$config = new Config($data = $this->getConfigData());
$this->assertNull($config->get('bar'));
$this->assertNull($config->get('bar.bam'));
}
/**
* @test
*/
public function testConfigGetShouldReturnDefault()
{
$config = new Config($data = $this->getConfigData());
$this->assertSame('baz', $config->get('bar', 'baz'));
$this->assertSame('baz', $config->get('bar.bam', 'baz'));
}
/**
* @test
*/
public function testConfigSet()
{
$config = new Config();
$config->set('foo.bar', 'baz');
$this->assertEquals(['bar' => 'baz'], $config->get('foo'));
$this->assertEquals('baz', $config->get('foo.bar'));
}
/**
* @test
*/
public function testConfigSetOverride()
{
$config = new Config();
$config->set('foo.bar', 'baz');
$this->assertEquals(['bar' => 'baz'], $config->get('foo'));
$this->assertEquals('baz', $config->get('foo.bar'));
$config->set('foo.bar.foo.bar.foo.bar', 'done');
$this->assertSame('done', $config->get('foo.bar.foo.bar.foo.bar'), 'foo.bar nested should be done');
$this->assertSame(['bar' => 'done'], $config->get('foo.bar.foo.bar.foo'));
$config->set('deep.deep', 'nested');
$this->assertEquals(['deep' => 'nested'], $config->get('deep'));
$this->assertEquals('nested', $config->get('deep.deep'));
$config->set('deep.deep.deep.deep.deep', 'nested');
$this->assertEquals('nested', $config->get('deep.deep.deep.deep.deep'));
$this->assertEquals(['deep' => 'nested'], $config->get('deep.deep.deep.deep'));
}
/**
* @test
*/
public function testConfigSetOverrideDefault()
{
$config = new Config($data = $this->getConfigData());
$this->assertEquals('nested', $config->get('deep.deep.deep.deep.deep'));
$config->set('deep.deep', 'new value');
$this->assertEquals('new value', $config->get('deep.deep'));
}
/**
* testConfigGetFromReference
*
* @access public
* @return mixed
*/
public function testConfigGetFromReference()
{
$config = $this->getMock('Acme\Configuration\Config', ['configGet']);
$config->expects($this->never())->method('configGet');
$config->set('foo.bar', ['baz' => 'some value']);
$config->set('foo.bar.baz', 'some value');
$this->assertEquals('some value', $config->get('foo.bar.baz'));
$this->assertEquals(['baz' => 'some value'], $config->get('foo.bar'));
}
protected function getConfigData()
{
return [
'foo' => [
'bar' => 'baz'
],
'zoom' => [
'somestuff' => 'some value'
],
'deep' => [
'deep' => [
'deep' => [
'deep' => [
'deep' => 'nested'
]
]
]
]
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment