I can easily write to and read from a sub-array in the session array.
$_SESSION['a']['b']['c']['value']=123;
$val=$_SESSION['a']['b']['c']['value'];
Instead of hard coding the "location" where the value is written, I would like it to be definable via a string or some other way. The following will obviously not work, but hopefully will better explain the intent.
$prefix="['a']['b']['c']"; //defined in config page, etc
$_SESSION.$prefix.['value']=123;
$val=$_SESSION.$prefix.['value'];
How can this be accomplished?
PropertyAccess
There is an excellent Symfony component for such tasks, named PropertyAccess. You can use it as follows:
$persons = array('a' => array('b' => 5.7));
$accessor = PropertyAccess::createPropertyAccessor();
echo $accessor->getValue($persons, '[a][b]'); // 5.7
You can install it using Composer as described in docs or fetch directly from GitHub.
Custom solution
This is a complete solution, I'm really impressed that it works... but it works! Check the code below, assert()'s demonstrate the usage:
<?php
function arrayPropertyPathGet(array $arr, $path) {
$parts = explode('.', $path);
$ret = $arr;
foreach($parts as $part) {
$ret = $ret[$part];
}
return $ret;
}
function arrayPropertyPathSet(array &$arr, $path, $value) {
$parts = explode('.', $path);
$tmp = &$arr;
foreach($parts as $part) {
if(!isset($tmp[$part])) { return false; }
$tmp = &$tmp[$part];
}
$tmp = $value;
return true;
}
$test = array('a' => array('b' => 'value'));
assert('value' === arrayPropertyPathGet($test, 'a.b'));
assert(true === arrayPropertyPathSet($test, 'a.b', 'other'));
assert('other' === arrayPropertyPathGet($test, 'a.b'));
Side note
As a theoretical side note (do not use this for anything other than learning purposes) you can experiment with eval(), such as:
eval("$value = $persons['a']['b']");
I faced the same problem a few times ago, and as I didn't find any solution, I made one by myself, if that can help you in anyway (only the interesting part) :
class ArrayAccessor {
private $prefix;
function setPrefix() {
$this->prefix = array();
for ($i = 0; $i < func_num_args(); ++$i) {
$this->prefix[] = func_get_arg($i);
}
}
function getFromPrefix(array $array) {
$tmp_array = $array;
foreach ($this->prefix as $pre) {
if (isset ($tmp_array[$pre])) {
$tmp_array = $tmp_array[$pre];
} else {
return null;
}
}
return $tmp_array;
}
}
$Access = new ArrayAccessor();
$Access->setPrefix('Hi', 'Toto');
$MyTestArray['Hi']['Toto'] = 'Works';
var_dump ($Access->getFromPrefix($MyTestArray));
$Access->setPrefix('Hi');
var_dump ($Access->getFromPrefix($MyTestArray));
$Access->setPrefix('No');
var_dump ($Access->getFromPrefix($MyTestArray));
Result :
string(5) "Works"
array(1) {
["Toto"]=>
string(5) "Works"
}
NULL
Related
In PHP we can do things like these:
Class Example {
...
}
$example = 'Example';
$object = new $example();
Or the use of variable variables:
$hour = 18;
$greets = array('Good morning','Good afternoon','Good evening');
$values = array(13,21,23);//people is sleeping at 23PM, so they don't greet.
$n = count($values);
$greet = 'greets';
for($i=0;$i<$n;$i++){
if($hour < $values[$i]){
echo 'hello, '.${$greet}[$i];
break;
}
}
And others..
I wonder if it would be possible to access directly to a specific index of a multidimensional array in a similar way. Something like:
$array = array(...); //multidimensional array.
$position = '[0][4][3]';
print_r($array$position);
Thanks in advance.
UPDATE
I'm so sorry because I finished my question in a wrong way.
I need to set the multimesional array and add a value. i.e:
$array$position = $data;
You could implement it yourself with a custom function:
function getValueFromMultiDimensionalArray( array $array, string $key )
{
$keys = explode('][', $key);
$value = $array;
foreach ($keys as $theKey) {
// remove the opening or closing bracket if present
$theKey = str_replace([ '[', ']' ], '', $theKey);
if (!isset($value[$theKey])) {
return null;
}
$value = $value[$theKey];
}
return $value;
}
You can define path as dot separated , check the following solution
function getValueByKey($a,$p){
$c = $a;
foreach(explode('.',$p) as $v){
if(!array_key_exists($v, $c)) return null;
$c = $c[$v];
}
return $c;
}
You can use this function as
$path = '1.2.3.0';
$indexValue = getValueByKey($array, $path);
Nope, this is not possible.
The only thing you can do is to implement ArrayAccess interface, which allows to access instances with [] operator. But you will have to define the logic yourself.
class MyClass implements ArrayAccess
{
...
}
$x = new MyClass([0=>[4=>[3=>'hello world']]]);
$position = '[0][4][3]';
echo $x[$position]; //hello world
I got a problem.
I make a function to update my config.json file.
The problem is, my config.json is a multdimensional array. To get a value of a key i use this function:
public function read($key)
{
$read = explode('.', $key);
$config = $this->config;
foreach ($read as $key) {
if (array_key_exists($key, $config)) {
$config = $config[$key];
}
}
return $config;
}
I also made a function to update a key. But the problem is if i make update('database.host', 'new value'); it dont updates only that key but it overrides the whole array.
This is my update function
public function update($key, $value)
{
$read = explode('.', $key);
$config = $this->config;
foreach ($read as $key) {
if (array_key_exists($key, $config)) {
if ($key === end($read)) {
$config[$key] = $value;
}
$config = $config[$key];
}
}
print_r( $config );
}
my config.json looks like this:
{
"database": {
"host": "want to update with new value",
"user": "root",
"pass": "1234",
"name": "dev"
},
some more content...
}
I have a working function but thats not really good. I know that the max of the indexes only can be three, so I count the exploded $key and update the value:
public function update($key, $value)
{
$read = explode('.', $key);
$count = count($read);
if ($count === 1) {
$this->config[$read[0]] = $value;
} elseif ($count === 2) {
$this->config[$read[0]][$read[1]] = $value;
} elseif ($count === 3) {
$this->config[$read[0]][$read[1]][$read[3]] = $value;
}
print_r($this->config);
}
Just to know: the variable $this->config is my config.json parsed to an php array, so nothing wrong with this :)
After I had read your question better I now understand what you want, and your read function, though not very clear, works fine.
Your update can be improved though by using assign by reference & to loop over your indexes and assign the new value to the correct element of the array.
What the below code does is assign the complete config object to a temporary variable newconfig using call by reference, this means that whenever we change the newconfig variable we also change the this->config variable.
Using this "trick" multiple times we can in the end assign the new value to the newconfig variable and because of the call by reference assignments the correct element of the this->config object should be updated.
public function update($key, $value)
{
$read = explode('.', $key);
$count = count($read);
$newconfig = &$this->config; //assign a temp config variable to work with
foreach($read as $key){
//update the newconfig variable by reference to a part of the original object till we have the part of the config object we want to change.
$newconfig = &$newconfig[$key];
}
$newconfig = $value;
print_r($this->config);
}
You can try something like this:
public function update($path, $value)
{
array_replace_recursive(
$this->config,
$this->pathToArray("$path.$value")
);
var_dump($this->config);
}
protected function pathToArray($path)
{
$pos = strpos($path, '.');
if ($pos === false) {
return $path;
}
$key = substr($path, 0, $pos);
$path = substr($path, $pos + 1);
return array(
$key => $this->pathToArray($path),
);
}
Please note that you can improve it to accept all data types for value, not only scalar ones
I have following url:
www.example.com/index.php/search/search_data/Doctor:a/Gender:Male/Language:Urdu/
and I want to convert it to associative array like
$data=array(
'Doctor'=> 'a',
'Gender'=> 'Male',
'Language'=> 'Urdu'
);
I have tried to do this using codeIgniter's URI class function
$this->uri->uri_to_assoc(n)
but as it accepts the data to be separated via '/' but I am having data with ':' as separator.
please help me.
I don't think there's an easier way to do this, rather than to do it manually.
First, retrieve the total segments, loop through, see if it contains ":", then add it into the array.
$segments = $this->uri->segment_array();
$search_array = array();
foreach($segments as $segment) {
if (strpos($segment, ":") !== FALSE) {
$e_array = explode(":", $segment);
$search_array[$e_array[0]] = $e_array[1];
}
}
Running that snippet somewhere will give you desirable results, $search_array will be an associative array with key => value.
You could hack the URI.php file. Change lines 431 - 439 to;
if (strpos($seg, ":") !== FALSE) {
list($parameter, $value) = explode(':', $seg);
if ($i % 2) {
$retval[$parameter] = $value;
} else {
$retval[$parameter] = $value;
$lastval = $seg;
}
} else {
if ($i % 2) {
$retval[$lastval] = $seg;
} else {
$retval[$seg] = FALSE;
$lastval = $seg;
}
}
Is it possible in PHP to extract values from an array with a particular key path and return an array of those values? I'll explain with an example:
$user =
array (
array(
'id' => 1,
'email' =>'asd#example.com',
'project' => array ('project_id' => 222, 'project_name' => 'design')
),
array(
'id' => 2,
'email' =>'asd2#example.com',
'project' => array ('project_id' => 333, 'project_name' => 'design')
)
);
/** I have to write a function something like: */
$projectIds = extractValuesWithKey($user, array('project', 'project_id'));
print_r($projectIds);
Output:
Array(
[0] => 222,
[1] => 333
)
I would have gone for a different approach (not that there's anything wrong with the array-function-based answers) by using a recursive iterator to flatten the array which makes the key-path comparison fairly simple.
function extractValuesWithKey($array, $keys) {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$keys_count = count($keys);
// No point going deeper than we have to!
$iterator->setMaxDepth($keys_count);
$result = array();
foreach ($iterator as $value) {
// Skip any level that can never match our keys
if ($iterator->getDepth() !== $keys_count) {
continue;
}
// Build key path to current item for comparison
$key_path = array();
for ($depth = 1; $depth <= $keys_count; $depth++) {
$key_path[] = $iterator->getSubIterator($depth)->key();
}
// If key paths match, add to results
if ($key_path === $keys) {
$result[] = $value;
}
}
return $result;
}
To make the whole thing more useful, you could even wrap the code into a custom FilterIterator rather than a basic function, but I guess that's probably a different question entirely.
Well, that's easier than you think.
function extractValuesWithKey($array, $parts) {
$return = array();
$rawParts = $parts;
foreach ($array as $value) {
$tmp = $value;
$found = true;
foreach ($parts as $key) {
if (!is_array($tmp) || !isset($tmp[$key])) {
$found = false;
continue;
} else {
$tmp = $tmp[$key];
}
}
if ($found) {
$return[] = $tmp;
}
}
return $return;
}
If the 'key path' isn't dynamic, you can do a one-liner with array_map:
$projectIds = array_map(function($arr) { return $arr['project']['project_id']; }, $user);
Alternatively, for dynamic paths:
function extractValuesWithKey($users, $path) {
return array_map(function($array) use ($path) {
array_walk($path, function($key) use (&$array) { $array = $array[$key]; });
return $array;
}, $users);
}
The closures/anonymous functions only work with PHP 5.3+, and I've no idea how this would compare performance-wise to a double foreach loop. Note also that there's no error checking to ensure that the path exists.
I also used a similiar function in one of my projects, maybe you find this useful:
function extractValuesWithKey($data, $path) {
if(!count($path)) return false;
$currentPathKey = $path[0];
if(isset($data[$currentPathKey])) {
$value = $data[$currentPathKey];
return is_array($value) ? extractValuesWithKey($value, array_slice($path, 1)) : $value;
}
else {
$tmp = array();
foreach($data as $key => $value) {
if(is_array($value)) $tmp[] = extractValuesWithKey($value, $path);
}
return $tmp;
}
}
How to send an indexes name for php array vairable.
the array is
$array = array('Somthing'=>array('More'=>array('id'=> 34)));
and now I want to display this thing but with a variable name I don't know how to explain so I write what I want to have.
$index_name = '[Something][More][id]';
$array{$index_name};
Is it possible in any way ?
Not in one go like that. Here's how you'd do it:
$array['Something']['More']['id']
If you particularly wanted access multidimensional arrays with a single string, then you could build a function to do that:
function array_multi(Array $arr, $path) {
$parts = explode(".", $path);
$curr =& $arr;
for ($i = 0, $l = count($parts); $i < $l; ++$i) {
if (!isset($curr[$parts[$i]])) {
// path doesn't exist
return null;
} else if (($i < $l - 1) && !is_array($curr[$parts[$i]]) {
// path doesn't exist
return null;
}
$curr =& $curr[$parts[$i]];
}
return $curr;
}
// usage:
echo array_multi($array, "Something.More.id"); // 34
echo array_multi($array, "Something.More"); // array("id" => 34)
Recursive version supporting your syntax with square brackets:
$array = array('Something'=>array('More'=>array('id'=> 34)));
$string = '[Something][More][id]';
echo scan_array($string, $array);
function scan_array($string, $array) {
list($key, $rest) = preg_split('/[[\]]/', $string, 2, PREG_SPLIT_NO_EMPTY);
if ( $key && $rest ) {
return scan_array($rest, $array[$key]);
} elseif ( $key ) {
return $array[$key];
} else {
return FALSE;
}
}
Ok, I know this is how people get shot. But c'mon, eval() is not always the wrong answer.
$array = array('Something'=>array('More'=>array('id'=> 34)));
$index_name = '[Something][More][id]';
eval('$val = $array'.$index_name.';'); // Wrap in a function or something
You could do this with eval():
<?php
$array = array('Somthing'=>array('More'=>array('id'=> 34)));
$index_name = "['Somthing']['More']['id']";
$stmt='echo $array'.$index_name.';';
eval($stmt);
?>
UPDATE:
It seems some SO users are uncomfortable with the idea of using eval(). I think it makes sense to read this thread which discusses the pros and cons before deciding whether to use this in your own code.
If you've cornered yourself into needing to do something like this, there's a pretty good chance you've done something else in a poor way. There's valid reasons to do this, but not very often.
function key_path($arr, $keys) {
return $keys ? key_path($arr[array_shift($keys)], $keys) : $arr;
}
$arr['Something']['More']['id'] = 34;
$keys = array('Something', 'More', 'id');
var_dump( key_path($arr, $keys));