Ответ 1
Используйте ссылочный оператор для получения последовательных существующих массивов:
$temp = &$data;
foreach($exploded as $key) {
$temp = &$temp[$key];
}
$temp = $value;
unset($temp);
У меня есть необычный случай использования, который я пытаюсь выполнить. Цель заключается в следующем: я хочу, чтобы клиент мог предоставить строку, например:
"cars.honda.civic = On"
Используя эту строку, мой код установит значение следующим образом:
$data['cars']['honda']['civic'] = 'On';
Достаточно легко отметить вход клиента как таковой:
$token = explode("=",$input);
$value = trim($token[1]);
$path = trim($token[0]);
$exploded_path = explode(".",$path);
Но теперь, как я могу использовать $exploded path для установки массива, не делая ничего противного, как eval?
Используйте ссылочный оператор для получения последовательных существующих массивов:
$temp = &$data;
foreach($exploded as $key) {
$temp = &$temp[$key];
}
$temp = $value;
unset($temp);
На основе ответа alexisdm:
/**
* Sets a value in a nested array based on path
* See /questions/192996/using-a-string-path-to-set-nested-array-data/1062207#1062207
*
* @param array $array The array to modify
* @param string $path The path in the array
* @param mixed $value The value to set
* @param string $delimiter The separator for the path
* @return The previous value
*/
function set_nested_array_value(&$array, $path, &$value, $delimiter = '/') {
$pathParts = explode($delimiter, $path);
$current = &$array;
foreach($pathParts as $key) {
$current = &$current[$key];
}
$backup = $current;
$current = $value;
return $backup;
}
Хорошо протестированный и 100% рабочий код. Установите, получите, не задайте значения из массива, используя "родители". Родители могут быть либо array('path', 'to', 'value')
, либо строка path.to.value
. На основе кода Drupal
/**
* @param array $array
* @param array|string $parents
* @param string $glue
* @return mixed
*/
function array_get_value(array &$array, $parents, $glue = '.')
{
if (!is_array($parents)) {
$parents = explode($glue, $parents);
}
$ref = &$array;
foreach ((array) $parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
$ref = &$ref[$parent];
} else {
return null;
}
}
return $ref;
}
/**
* @param array $array
* @param array|string $parents
* @param mixed $value
* @param string $glue
*/
function array_set_value(array &$array, $parents, $value, $glue = '.')
{
if (!is_array($parents)) {
$parents = explode($glue, (string) $parents);
}
$ref = &$array;
foreach ($parents as $parent) {
if (isset($ref) && !is_array($ref)) {
$ref = array();
}
$ref = &$ref[$parent];
}
$ref = $value;
}
/**
* @param array $array
* @param array|string $parents
* @param string $glue
*/
function array_unset_value(&$array, $parents, $glue = '.')
{
if (!is_array($parents)) {
$parents = explode($glue, $parents);
}
$key = array_shift($parents);
if (empty($parents)) {
unset($array[$key]);
} else {
array_unset_value($array[$key], $parents);
}
}
$data = $value;
foreach (array_reverse($exploded_path) as $key) {
$data = array($key => $data);
}
Основываясь на ответе Уго Меды:
Эта версия
Cannot create references to/from string offsets nor overloaded objects
)нет фатальной ошибки
$a = ['foo'=>'not an array'];
arrayPath($a, ['foo','bar'], 'new value');
$a
теперь
array(
'foo' => array(
'bar' => 'new value',
),
)
Использовать как getter
$val = arrayPath($a, ['foo','bar']); // returns 'new value' / $a remains the same
Установить значение в значение null
$v = null; // assign null to variable in order to pass by reference
$prevVal = arrayPath($a, ['foo','bar'], $v);
$prevVal
- "новое значение" $a
теперь
array(
'foo' => array(
'bar' => null,
),
)
/**
* set/return a nested array value
*
* @param array $array the array to modify
* @param array $path the path to the value
* @param mixed $value (optional) value to set
*
* @return mixed previous value
*/
function arrayPath(&$array, $path = array(), &$value = null)
{
$args = func_get_args();
$ref = &$array;
foreach ($path as $key) {
if (!is_array($ref)) {
$ref = array();
}
$ref = &$ref[$key];
}
$prev = $ref;
if (array_key_exists(2, $args)) {
// value param was passed -> we're setting
$ref = $value; // set the value
}
return $prev;
}
Вам нужно использовать Symfony PropertyPath
<?php
// ...
$person = array();
$accessor->setValue($person, '[first_name]', 'Wouter');
var_dump($accessor->getValue($person, '[first_name]')); // 'Wouter'
// or
// var_dump($person['first_name']); // 'Wouter'
Хорошо, я столкнулся с подобной проблемой, и это спасло мне жизнь. Очень гибкий подход. Я не могу вставить здесь все решение, так как оно длинное, но в этой ссылке есть все. Genius!
https://kvz.io/blog/2007/10/03/convert-anything-to-tree-structures-in-php/
Это именно то, для чего этот метод :
Arr::set($array, $keys, $value);
Он берет ваш $array
, где должен быть установлен элемент, и принимает $keys
в формате с разделением точками или массив последующих ключей.
Таким образом, в вашем случае вы можете достичь желаемого результата просто:
$data = Arr::set([], "cars.honda.civic", 'On');
// Which will be equivalent to
$data = [
'cars' => [
'honda' => [
'civic' => 'On',
],
],
];
Более того, параметр $keys
также может принимать создание автоматического индекса, поэтому вы можете, например, использовать его следующим образом:
$data = Arr::set([], "cars.honda.civic.[]", 'On');
// In order to get
$data = [
'cars' => [
'honda' => [
'civic' => ['On'],
],
],
];
Разве вы не можете это сделать
$exp = explode(".",$path);
$array[$exp[0]][$exp[1]][$exp[2]] = $value