I\'ve to implement a setter in PHP, that allows me to specify the key, or sub key, of an array (the target), passing the name as a dot-separated-keys value.
I have a really simple and dirty solution to this (really dirty! DO NOT use if the value of the key is untrusted!). It might be more efficient than looping through the array.
function array_get($key, $array) {
return eval('return $array["' . str_replace('.', '"]["', $key) . '"];');
}
function array_set($key, &$array, $value=null) {
eval('$array["' . str_replace('.', '"]["', $key) . '"] = $value;');
}
Both of these functions do an eval on a snippet of code where the key is converted to an element of the array as PHP code. And it returns or sets the array value at the corresponding key.
Here a simple code to access and manipulate MD array. But there's no securities.
setter :
eval('$vars = &$array["' . implode('"]["', explode('.', strtolower($dot_seperator_path))) . '"];');
$vars = $new_value;
getter:
eval('$vars = $array["' . implode('"]["', explode('.', strtolower($dot_seperator_path))) . '"];');
return $vars;
As a "getter", I've used this in the past:
$array = array('data' => array('one' => 'first', 'two' => 'second'));
$key = 'data.one';
function find($key, $array) {
$parts = explode('.', $key);
foreach ($parts as $part) {
$array = $array[$part];
}
return $array;
}
$result = find($key, $array);
var_dump($result);
I have a utility I regularly use that I'll share. The difference being it uses array access notation (e.g. b[x][z]
) instead of dot notation (e.g. b.x.z
). With the documentation and code it is fairly self-explanatory.
<?php
class Utils {
/**
* Gets the value from input based on path.
* Handles objects, arrays and scalars. Nesting can be mixed.
* E.g.: $input->a->b->c = 'val' or $input['a']['b']['c'] = 'val' will
* return "val" with path "a[b][c]".
* @see Utils::arrayParsePath
* @param mixed $input
* @param string $path
* @param mixed $default Optional default value to return on failure (null)
* @return NULL|mixed NULL on failure, or the value on success (which may also be NULL)
*/
public static function getValueByPath($input,$path,$default=null) {
if ( !(isset($input) && (static::isIterable($input) || is_scalar($input))) ) {
return $default; // null already or we can't deal with this, return early
}
$pathArray = static::arrayParsePath($path);
$last = &$input;
foreach ( $pathArray as $key ) {
if ( is_object($last) && property_exists($last,$key) ) {
$last = &$last->$key;
} else if ( (is_scalar($last) || is_array($last)) && isset($last[$key]) ) {
$last = &$last[$key];
} else {
return $default;
}
}
return $last;
}
/**
* Parses an array path like a[b][c] into a lookup array like array('a','b','c')
* @param string $path
* @return array
*/
public static function arrayParsePath($path) {
preg_match_all('/\\[([^[]*)]/',$path,$matches);
if ( isset($matches[1]) ) {
$matches = $matches[1];
} else {
$matches = array();
}
preg_match('/^([^[]+)/',$path,$name);
if ( isset($name[1]) ) {
array_unshift($matches,$name[1]);
} else {
$matches = array();
}
return $matches;
}
/**
* Check if a value/object/something is iterable/traversable,
* e.g. can it be run through a foreach?
* Tests for a scalar array (is_array), an instance of Traversable, and
* and instance of stdClass
* @param mixed $value
* @return boolean
*/
public static function isIterable($value) {
return is_array($value) || $value instanceof Traversable || $value instanceof stdClass;
}
}
$arr = array('a' => 1,
'b' => array(
'y' => 2,
'x' => array('z' => 5, 'w' => 'abc')
),
'c' => null);
$key = 'b[x][z]';
var_dump(Utils::getValueByPath($arr,$key)); // int 5
?>