Reputation: 3382
I have a bunch of strings like "memory.caching" and "server.base.url". Within my configuration object each part of the key "." is equal to a key within an array, the value could be another array and the last would be the value so "memory.caching" would equate to.
$config = array(
"memory" => array(
"caching" => true
)
);
I want to create a setter method. I have ended up with the below code, but that won't work for three or four levels of depth. How can I do it without adding multiple else/if clauses.
public function set($k, $v) {
$parts = explode(".", $k);
// Start sucky code.
if (count($parts) == 2)
{
$this->config[$parts[0]][$parts[1]] = $val;
}
}
I was thinking some form of loop with assigning by reference like below, but I couldn't get it to work.
public function set($k, $v) {
$parts = explode(".", $k);
$tmp = $this->config;
foreach ($parts as $p) {
$tmp &= $tmp[$p];
}
$tmp = $v;
}
Any ideas on how I could achieve this?
Upvotes: 1
Views: 559
Reputation: 20592
While this question has since been answered (and accepted) I'll just throw this version out there for future visitors:
(Might be missing some checks, I have a few versions of this kicking around)
class Map
{
protected $_array = array();
protected $_separator;
public static function get(Array $array, $key, $separator)
{
$parts = explode($separator, $key);
$key = array_shift($parts);
while (!empty($parts))
{
if (!isset($array[$key]) || !is_array($array[$key]))
{
return null;
}
$array = &$array[$key];
$key = array_shift($parts);
}
return isset($array[$key]) ? $array[$key] : null;
}
public static function has(Array $array, $key, $separator)
{
$parts = explode($separator, $key);
$key = array_shift($parts);
while (!empty($parts))
{
if (!isset($array[$key]) || !is_array($array[$key]))
{
return false;
}
$array = &$array[$key];
$key = array_shift($parts);
}
return isset($array[$key]);
}
public static function set(&$array, $key, $value, $separator)
{
$parts = explode($separator, $key);
$key = array_shift($parts);
while (!empty($parts))
{
if (!isset($array[$key]) || !is_array($array[$key]))
{
$array[$key] = array();
}
$array = &$array[$key];
$key = array_shift($parts);
}
$array[$key] = $value;
}
public static function bind(&$array, $key, &$variable, $separator)
{
$parts = explode($separator, $key);
$key = array_shift($parts);
while (!empty($parts))
{
if (!isset($array[$key]) || !is_array($array[$key]))
{
$array[$key] = array();
}
$array = &$array[$key];
$key = array_shift($parts);
}
if (isset($array[$key]))
{
$variable = $array[$key];
}
$array[$key] = &$variable;
}
public static function remove(&$array, $key, $separator)
{
$parts = explode($separator, $key);
$key = array_shift($parts);
while (!empty($parts))
{
if (!isset($array[$key]) || !is_array($array[$key]))
{
return;
}
$array = &$array[$key];
$key = array_shift($parts);
}
unset($array[$key]);
}
public function __construct(&$array, $separator)
{
if (!is_array($array))
{
$array = array();
}
$this->_array = $array;
$this->_separator = (string) $separator;
}
public function __get($key)
{
return static::get($this->_array, $key, $this->_separator);
}
public function __isset($key)
{
return static::has($this->_array, $key, $this->_separator);
}
public function __set($key, $value)
{
static::set($this->_array, $key, $value, $this->_separator);
}
public function __unset($key)
{
static::remove($this->_array, $key, $this->_separator);
}
public function get_array()
{
return $this->_array;
}
}
And use it like:
$array = array(
'foo' => array(
'bar' => array(
'hello' => 'world',
),
),
);
$map = new Map($array, '.');
var_dump($map->{'foo.bar.hello'});
//string(5) "world"
$map->{'foo.bar.goodbye'} = 'universe';
unset($map->{'foo.bar.hello'});
var_dump($map->get_array());
// array(1) {
// ["foo"]=>
// array(1) {
// ["bar"]=>
// array(1) {
// ["goodbye"]=>
// string(8) "universe"
// }
// }
// }
var_dump(isset($map->{'foo.bar.goodbye'}));
// true
Bind is very useful, but doesn't have a magic method to alias it with semantically.
Upvotes: 0
Reputation: 490173
To set a value...
public function set($path, $value, $delimiter = '.') {
$tokens = explode($delimiter, $path);
$currentPiece = &$this->config;
foreach($tokens as $token) {
$currentPiece = &$currentPiece[$token];
}
$currentPiece = $value;
}
To get a value...
public function get($path, $delimiter = '.') {
$tokens = explode($delimiter, $path);
$currentPiece = $this->config;
while ($token = array_shift($tokens)) {
$currentPiece = $currentPiece[$token];
}
return $currentPiece;
}
Upvotes: 2
Reputation: 88647
One slightly horrible, but definitely works approach to this is to use eval()
(evil):
public function set($k, $v) {
eval('$this->config["'.implode('"]["', explode(".", $k)).'"] = $v;');
}
Upvotes: 0