Подтвердить что ты не робот

Использование строкового пути для установки данных вложенных массивов

У меня есть необычный случай использования, который я пытаюсь выполнить. Цель заключается в следующем: я хочу, чтобы клиент мог предоставить строку, например:

"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?

4b9b3361

Ответ 1

Используйте ссылочный оператор для получения последовательных существующих массивов:

$temp = &$data;
foreach($exploded as $key) {
    $temp = &$temp[$key];
}
$temp = $value;
unset($temp);

Ответ 2

На основе ответа 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;
}

Ответ 3

Хорошо протестированный и 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);
    }
}

Ответ 4

$data = $value;
foreach (array_reverse($exploded_path) as $key) {
    $data = array($key => $data);
}

Ответ 5

Основываясь на ответе Уго Меды:

Эта версия

  • позволяет использовать его исключительно как getter (оставить исходный массив нетронутым)
  • исправляет фатальную ошибку, если встречается значение, отличное от массива (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;
}

Ответ 6

Вам нужно использовать 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'

Ответ 7

Хорошо, я столкнулся с подобной проблемой, и это спасло мне жизнь. Очень гибкий подход. Я не могу вставить здесь все решение, так как оно длинное, но в этой ссылке есть все. Genius!

https://kvz.io/blog/2007/10/03/convert-anything-to-tree-structures-in-php/

Ответ 8

Это именно то, для чего этот метод :

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'],
    ],
  ],
];

Ответ 9

Разве вы не можете это сделать

$exp = explode(".",$path);
$array[$exp[0]][$exp[1]][$exp[2]] = $value