PHP array_replace_recursive 如果是标量,array_merge_recursive if 数


PHP array_replace_recursive if scalar, array_merge_recursive if array

我有一些默认配置,以及一些可配置的特定配置。我需要将特定配置合并到默认配置中。

  • 如果特定配置选项不存在,则将使用默认选项。
  • 如果value是标量,则应应用特定配置
  • 如果value是标量数组,则应合并并array_unique应用数组。
  • value是关联数组的情况下,我们需要应用上述scalarscalar_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' => [
          ...
      ]
   ],
];

有没有一种很好的干净方法来实现这一目标?

此函数获得所需的结果。它假定特定类型与默认类型是一致的,因此不执行一致性检查。函数迭代特定的配置数组并检查对应的默认值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 枚举/关联数组检查基于此答案。

我的情况略有不同,但可能会有所帮助。我需要替换数组上的标量和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;
    }
}

我从第一个答案重写了函数,以便与配置数组一起使用:

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