If I do this:
$array = ["one", "two", "three"];
$array_copy = "$array"; // or "{$array}"
I doesn't get the original array but instead a string conversion of it. ¿Is there any way to acomplish this task? To get the array reference by his string name.
Thank you.
Edit:
I am aware of this:
$array = ["one", "two", "three"];
$array_copy = $"array";
or
$name = "array";
$array_copy = $$name
But I need to achive this in any situation. Example:
$array = ["one", "two", "three" => ["four"] ];
$sub_array = "{$array['three']}"; // this returns an string, not the array ["four"]
I hope is more clear now.
Edit 2
Let's put it in other way. Imagine you need that an user input (string) be able to access the content of any declared variable. Example:
$customer = [ "name"=>"Peter", "address"=> ["street"=>"5th", "number"=>1969] ];
$variable_name = $_GET["varname"];
var_export( $$variable_name ); // What can write the user to print $customer["address"] array?
because you equal to string, just do it directly
$array_copy = $array
but copy is just copy, not a referense, if you want reference you should write like this
$array_copy = &$array
but there are should be reasons to get the reference
or if you have some variable with array name then you can do like this
$array = ["one", "two", "three"];
$arrayName = 'array';
$array_copy = $$arrayName;
You may use a function that takes a path such as customer.address as a parameter to retrieve the address index of the $customer array automatically:
$customer = ['name' => 'Peter', 'address' => ['street' => '5th', 'number' => 1969]];
/**
* #param array $array
* #param string $path A dot-separated property path.
* #param mixed $default
* #return mixed
*/
function getArrayValue(array $array, string $path, $default = null)
{
$parts = explode('.', $path);
return array_reduce($parts, static function ($value, $part) use ($default) {
return $value[$part] ?? $default;
}, $array);
}
/**
* #param string $path A dot-separated path, whose first part is a var name available in the global scope.
* #param mixed $default
* #return mixed
*/
function getGlobalArrayValue(string $path, $default = null)
{
#list($varName, $propertyPath) = explode('.', $path, 2);
return getArrayValue($GLOBALS[$varName] ?? [], $propertyPath, $default);
}
echo getGlobalArrayValue('customer.name'), PHP_EOL; // Peter
echo getGlobalArrayValue('customer.address.street'), PHP_EOL; // '5th'
echo getGlobalArrayValue('customer.address.idontexist', 'somedefaultvalue'), PHP_EOL; // 'somedefaultvalue'
echo getGlobalArrayValue('idontexist.address', 12); // 12
Demo: https://3v4l.org/O6h2P
Related
I have this variable:
$otcId;
which value can be retrieved from 3 different places(keep in mind this value will always be the same in all places):
$otcId = $this->dat['id_rfcOV'];
$otcId = $this->request['id_rfcOV'];
$otcId = $this->response['id_rfcOV'];
my approach was the following:
$otcId = $this->dat['id_rfcOV'];
if(isset($this->dat['id_rfcOV'])){
$otcId = $this->dat['id_rfcOV'];
} elseif(isset($this->request['id_rfcOV'])) {
$otcId = $this->request['id_rfcOV'];
} elseif(isset($this->response['id_rfcOV'])) {
$otcId = $this->response['id_rfcOV'];
}
what would be a shorter better and more readable way to write this code?
If I were you, I would wrap this in a function, and use the null coalescing operator to simplify retrieval and the return of a default value.
<?php
class MyController
{
private array $dat = [];
private array $request = ['id_rfcOV' => 'foo'];
private array $response = ['id_rfcOV' => 'bar'];
/**
* #param string $name Parameter name
* #param null $default Default value to return if no matching parameters are found
* #return mixed|string|null
*/
function getParam(string $name, $default=null)
{
return $this->dat[$name] ?? $this->request[$name] ?? $this->response[$name] ?? $default;
}
function test(): void
{
// Using a member function, we can get our parameter value with a one-liner
$otcId = $this->getParam('id_rfcOV');
assert($otcId == 'bar', 'Value should be from the last array checked');
printf("Value is %s \n", $otcId);
$val = $this->getParam('non-existent', 'wombats');
assert($val == 'wombats', 'Value should be the default');
printf("Value is %s \n", $val);
}
}
$myController = new MyController();
$myController->test();
I have a recursive $data structure that I need to modify. Each node considered an $item should get a property with the value of $value added. Things that I tried (and how they failed) are:
array_walk_recursive: Visits only leaf nodes.
Stack/queue: I failed to modify the original structure but only altered the copies on the stack/queue.
Loops: Without the stack/queue approach I would need to know the nesting level and write an awful lot of nested loops.
array_map: I failed to write a proper recursive callback given that the value of $value is not static but the result of previous code. So it must somehow get "into" the callback. Since use is only available to anonymous functions I did not manage to write a recursive one.
Loop and recursive function: This answer to a similar question failed for the same reason as the array_map approach.
My situation in code looks similar to this example:
<?php
$value = 'example';
$data = array(
'foo' => 'bar'
'items' => array(
array(
'foo' => 'bar',
'items' => array(
array('foo' => 'bar')
)
)
)
);
// do this recursively to every member of an 'items' property:
$item['baz'] = $value;
Can you think of a different approach or help me straighten out one of those that I failed at so far?
Update
Some code that I tried that did not work:
// Parse error: syntax error, unexpected 'use' (T_USE), expecting '{'
function do (&$item) use ($value) {
$item['baz'] = $value;
foreach ($item['items'] as $next) {
do($next);
}
}
// Undefined variable: value
function do (&$item) {
$item['baz'] = $value;
foreach ($item['items'] as $next) {
do($next);
}
}
foreach ($data['items'] as $item) {
do($item);
}
Works for now (I would prefer not having to pass the $value parameter, though):
function do (&$item, $value) {
$item['baz'] = $value;
foreach ($item['items'] as &$next) {
do($next, $value);
}
}
foreach ($data['items'] as &$item) {
do($item, $value);
}
Check this code for get each key and value:
<?php
error_reporting(0);
$value = 'example';
$data = array(
'foo' => 'bar',
'items' => array( array( 'foo' => 'bar','items' => array(array('foo' => 'bar') ) ) )
);
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($data));
foreach ($iterator as $k => $v) {
echo $k.'=>'.$v;
echo '</br>';
}
?>
Formatting was necessary but did not suffice for the minimum edit length. So I added this otherwise useless text.
The following recursive function works for me. Note that it requires to pass parameters by reference also inside the foreach loop:
$value = 'example';
function do (&$item, $value) {
$item['baz'] = $value;
foreach ($item['items'] as &$next) {
do($next, $value);
}
}
foreach ($data['items'] as &$item) {
do($item, $value);
}
I use this method:
<?php
/**
* #param array $arr
* #param callable $callback
* #param array $options
*
*
* Example:
* (this will add the link property to every node in the array recursively)
*
*
* $linkFmt = "/mylink/{type}/{slug}";
* ArrayTool::updateNodeRecursive($ret, function (array &$row) use ($linkFmt) {
* $row['link'] = str_replace([
* "{type}",
* "{slug}",
* ], [
* $row['type'],
* $row['slug'],
* ], $linkFmt);
* });
*
*
*
*
*/
public static function updateNodeRecursive(array &$arr, callable $callback, array $options = [])
{
$childrenKey = $options['childrenKey'] ?? "children";
foreach ($arr as $k => $v) {
call_user_func_array($callback, [&$v]);
if (array_key_exists($childrenKey, $v) && $v[$childrenKey]) {
$children = $v[$childrenKey];
self::updateNodeRecursive($children, $callback, $options);
$v[$childrenKey] = $children;
}
$arr[$k] = $v;
}
}
How can I fix this method to accept reference passing?
It throws Fatal error:Only variables can be passed by reference.
/**
* Set a value "deep within" an associative array.
*
* #param array $array_ - Target array to set value on.
* #param mixed $value - A value to set.
* #param string $keyPath - Like 'campaign.pushMessages.sendDate'.
*/
private function setValueForKeyPath(&$array, $value, $keyPath)
{
$keys = explode(".", $keyPath, 2); // Like [ 'campaign', 'pushMessages.sendDate' ]
// If keys is a leaf key...
$isLeaf = (count($keys) == 1);
if ($isLeaf)
{
// ...simply set the value.
$array[$keys[0]] = $value;
}
else
{
// ...or set a sub-array as value.
$this->setValueForKeyPath($array[$keys[0]], $value, $keys[1]);
}
}
I am trying to figure out the best way to use dot notation when passing in a key or set of keys into a function and getting that post value.
Example
shipping.first_name
What it looks like in actual $_POST array:
$_POST[shipping][first_name] = 'some value'
I would like to be able to pass in (as a parameter) the string, and have the function return the post value.
function get_post($str = NULL){
return $_POST[$key1][$key1]..etc.
}
Current attempt (working as intended, but need to put into $_POST):
From: SO Question
function assignArrayByPath(&$arr, $path) {
$keys = explode('.', $path);
while ($key = array_shift($keys)) {
$arr = &$arr[$key];
}
}
$output = array();
assignArrayByPath($output, $str);
This produces an array of:
Array ( [shipping] => Array ( [first_name] => ) )
I would like then to do something like this:
return isset($_POST.$output) ? true : false;
So how do I take that array created from the period separated string and check if it exists in POST?
I think this might be a duplicate, but I am not positive. I apologize in advance if it is. Any help is much appreciated.
See Laravel array_set implement http://laravel.com/api/source-function-array_set.html#319
/**
* Set an array item to a given value using "dot" notation.
*
* If no key is given to the method, the entire array will be replaced.
*
* #param array $array
* #param string $key
* #param mixed $value
* #return array
*/
function array_set(&$array, $key, $value)
{
if (is_null($key)) return $array = $value;
$keys = explode('.', $key);
while (count($keys) > 1)
{
$key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
// values at the correct depth. Then we'll keep digging into the array.
if ( ! isset($array[$key]) || ! is_array($array[$key]))
{
$array[$key] = array();
}
$array =& $array[$key];
}
$array[array_shift($keys)] = $value;
return $array;
}
Check exists you can see array_get http://laravel.com/api/source-function-array_get.html#224
/**
* Get an item from an array using "dot" notation.
*
* #param array $array
* #param string $key
* #param mixed $default
* #return mixed
*/
function array_get($array, $key, $default = null)
{
if (is_null($key)) return $array;
if (isset($array[$key])) return $array[$key];
foreach (explode('.', $key) as $segment)
{
if ( ! is_array($array) || ! array_key_exists($segment, $array))
{
return value($default);
}
$array = $array[$segment];
}
return $array;
}
Is there a way to reference an item within a multidimensional array by using a path or an array of path elements? EG.
$multi = array
(
'array_1' => array
(
'array_2' => array
(
'option_1' => 'value_1',
'option_2' => 'value_2',
)
)
);
$path = array('level_1', 'level_2', 'option_1');
$result = $multi[$path];
And have $result = 'value_1'?
The reason being, I have a recursive function for searching thru $multi and finding the key I need, and returning the $path. I know i can hard code in the path from my own code but i'm trying to make this reusable so that i can edit the $multi and the function will still work.
There's nothing built into PHP to do this but you can write a function for it, using a moving reference:
/**
* #param string $path path in the form 'item_1.item_2.[...].item_n'
* #param array $array original array
*/
function &get_from_array($path, &$array)
{
$current =& $array;
foreach(explode('.', $path) as $key) {
$current =& $current[$key];
}
return $current;
}
Example:
// get element:
$result = get_from_array('level_1.level_2.option_1', $multi);
echo $result; // --> value_1
$result = 'changed option';
echo $multi['level_1']['level_2']['option_1']; // --> changed_option
I wrote it to convert names from configuration files to arrays, if you want to pass the path itself as an array like in your example, just leave out the explode.