PHP - Get array reference by his string name - php

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

PHP - Avoid writting nested if statement

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();

How to make array_walk_recursive visit every node?

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;
}
}

Pass array as reference in PHP 5.2.13?

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]);
}
}

PHP turn string into php post indexes

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;
}

Using a path to an array item

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.

Categories