PHP array_replace_recursive if scalar, array_merge_recursive if array
Asked Answered
G

3

7

I have some default configurations, and some specific configurations which would be configurable. I need to merge the specific configurations into the default configurations.

  • In the case that the specific config option does not exist, the default option will be used.
  • In the case that the value is a scalar, the specific configuration should be applied
  • In the case that the value is a scalar array, the arrays should be merged and array_unique applied.
  • In the case that the value is an associative array, We need to apply the above scalar and scalar_array rules.

Example:

$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' => [
          ...
      ]
   ],
];

Expected Output:

$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' => [
          ...
      ]
   ],
];

Is there a nice clean way of achieving this?

Greisen answered 22/2, 2016 at 11:40 Comment(3)
This is interesting, if I understood, you are trying something like SublimeText configuration files (just to make it clear)?Scutter
I think you understood it exactlyGreisen
I would go with a custom recursive function, because you might need more features than the php recursive functions can have. But anyway I personally find it completly ok.Scutter
C
4

This function obtains your desired result. It assume that specific types are coherents with default types, so no coherence check is performed. The function iterate specific configuration array and check corresponding default value1: if it is scalar, replace default value; if it is a enumerated array2, it merge unique values; otherwise function call itself with current values as arguments.

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

Calling the function in this way:

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

$result, applied to your arrays sample, is this:

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

With this array couple:

$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 is:

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

        )

)

Notes:

1 Yes, this is a bit incoherent: I felt was better iterate through specific array (not existent items remain untouched), but performing value check on default array, that is the reference point.

2 The enumerated/associative array check is based on this answer.

Convection answered 1/4, 2016 at 22:37 Comment(0)
M
1

My case was slightly different but it might be of help. I needed to replace scalars and array_merge_recursive on arrays.

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;
    }
}
Madelle answered 31/3, 2016 at 7:12 Comment(0)
K
0

I rewrote function from the first answer a bit for using with array of configs:

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);
}
Kinsella answered 15/4, 2018 at 9:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.