PHP array_replace_recursive, если скаляр, array_merge_recursive, если массив

У меня есть некоторые конфигурации по умолчанию и некоторые конкретные конфигурации, которые можно было бы настроить. Мне нужно объединить конкретные конфигурации в конфигурации по умолчанию.

  • В случае, если конкретный параметр конфигурации не существует,
    будет использоваться опция по умолчанию.
  • В случае, если value это скаляр, конкретная конфигурация должна быть применена
  • В случае, если value является скалярным массивом, массивы должны быть объединены и применен array_unique.
  • В случае, если value это ассоциативный массив, нам нужно применить выше scalar а также scalar_array правила.

Пример:

$defaultConfigs = [
'scalar1' => 1,
'scalar2' => "Apple",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => 1,
'array_scalar' => [1,2,3],
'array_associative' => [
...
]
],
];

$specificConfigs = [
'scalar1' => "A",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => 1,
'array_scalar' => [1,2,3],
'array_associative' => [
...
]
],
];

Ожидаемый результат:

$expectedConfigs = [
'scalar1' => "A",                  // Overridden
'scalar2' => "Apple",              // Default used
'array_scalar' => [1,2,3,4,5],     // Scalar merged and array_unique
'array_associative' => [
'scalar' => "B",                // Overridden
'array_scalar' => [1,2,3,4,5],  // Scalar merged and array_unique
'array_associative' => [
...
]
],
];

Есть ли хороший чистый способ достижения этого?

7

Решение

Эта функция получает желаемый результат. Предполагается, что определенные типы являются когерентами с типами по умолчанию, поэтому проверка когерентности не выполняется. Функция выполняет итерацию определенного массива конфигурации и проверяет соответствующее значение по умолчанию1: если это скаляр, замените значение по умолчанию; если это перечислимый массив2, объединяет уникальные ценности; в противном случае функция вызывает себя с текущими значениями в качестве аргументов.

function fillConfig( $default, $specific )
{
foreach( $specific as $key=> $val )
{
if( isset( $default[$key] ) )
{
if( ! is_array( $default[$key] ) )
{
$default[$key] = $val;
}
elseif( array_keys($default[$key]) === range(0, count($default[$key]) - 1) )
{
$default[$key] = array_unique( array_merge( $default[$key], $val ) );
}
else
{
$default[$key] = fillConfig( $default[$key], $val );
}
}
else
{
// This happens when a specific key doesn't exists in default configuration.
// I think that in this case the value must be omitted,
// otherwise you can un-comment following line:
// $default[$key] = $val;
}
}
return $default;
}

Вызов функции следующим образом:

$result = fillConfig( $defaultConfigs, $specificConfigs );

$resultприменяется к вашему образцу массивов, это:

Array
(
[scalar1] => A
[scalar2] => Apple
[array_scalar] => Array
(
[0] => 3
[1] => 4
[2] => 5
)
[array_associative] => Array
(
[scalar] => 1
[array_scalar] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[array_associative] => Array
(
)
)
)

С этим массивом пара:

$defaultConfigs = [
'scalar1' => 1,
'scalar2' => "Apple",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => 1,
'array_scalar' => [1,2,3],
'array_associative' => [

]
],
];

$specificConfigs = [
'scalar1' => "A",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => B,
'array_scalar' => [3,4,5],
'array_associative' => [

]
],
];

$result является:

Array
(
[scalar1] => A
[scalar2] => Apple
[array_scalar] => Array
(
[0] => 3
[1] => 4
[2] => 5
)

[array_associative] => Array
(
[scalar] => B
[array_scalar] => Array
(
[0] => 1
[1] => 2
[2] => 3
[4] => 4
[5] => 5
)

[array_associative] => Array
(
)

)

)

Заметки:

1 Да, это немного противоречиво: я чувствовал, что лучше перебирать конкретный массив (не существующие элементы остаются без изменений), но выполнять проверку значения в массиве по умолчанию, который является контрольной точкой.

2 Проверка перечислимого / ассоциативного массива основана на этот ответ.

3

Другие решения

Мой случай немного отличался, но это могло бы помочь. Мне нужно было заменить скаляры и array_merge_recursive на массивах.

class ArrayUtil {

public static function mergeRecursive(array $array1, $array2) {
if($array2 && is_array($array2)) {
foreach($array2 as $key => $val2) {
if (is_array($val2) && (null!==($val1 = isset($array1[$key]) ? $array1[$key] : null)) && is_array($val1)) {
$array1[$key] = self::mergeRecursive($val1,$val2);
} else {
$array1[$key] = $val2;
}
}
}
return $array1;
}
}
1

Я переписал функцию из первого ответа немного для использования с массивом конфигов:

private function mergeConfigs(array $configs): array
{
$default = array_shift($configs);
return array_reduce($configs, function (array $result, array $config) {
foreach ($config as $key => $val) {
if (!isset($result[$key]) || !is_array($result[$key])) {
$result[$key] = $val;
continue;
}
$result[$key] = array_keys($result[$key]) === range(0, count($result[$key]) - 1)
? array_unique(array_merge($result[$key], $val))
: $this->mergeConfigs([$result[$key], $val]);
}
return $result;
}, $default);
}
0
По вопросам рекламы [email protected]