You are here

public static function ConfigMerger::mergeConfigItemStates in Config Merge 8

Merges changes to a configuration item into the active storage.

Parameters

array $previous: The configuration item as previously provided (from snapshot).

array $current: The configuration item as currently provided by an extension.

array $active: The configuration item as present in the active storage.

array $parent_keys: The keys of the property being merged in case of nested structure.

int $level: The level of recursion.

Return value

array

File

src/ConfigMerger.php, line 68

Class

ConfigMerger
Provides helper functions for merging configuration items.

Namespace

Drupal\config_merge

Code

public static function mergeConfigItemStates(array $previous, array $current, array $active, $parent_keys = [], $level = 0) {
  if ($level === 0) {
    self::$logs = [];
  }

  // We are merging into the active configuration state.
  $result = $active;
  $states = [
    $previous,
    $current,
    $active,
  ];
  $is_associative = FALSE;
  foreach ($states as $array) {

    // Analyze the array to determine if we should preserve integer keys.
    if (Inline::isHash($array)) {

      // If any of the states is associative, treat the item as associative.
      $is_associative = TRUE;
      break;
    }
  }

  // Process associative arrays.
  // Find any differences between previous and current states.
  if ($is_associative) {

    // Detect and process removals.
    $removed = array_diff_key($previous, $current);
    foreach ($removed as $key => $value) {

      // Remove only if unchanged in the active state.
      if (isset($active[$key]) && $active[$key] === $previous[$key]) {
        unset($result[$key]);
      }
    }

    // Detect and handle additions.
    // Additions are keys added since the previous state and not overridden
    // in the active state.
    $added = array_diff_key($current, $previous, $active);

    // Merge in all current keys while retaining the key order.
    $merged = array_replace($current, $result);

    // Filter to keep array items from the merged set that ...
    $result = array_intersect_key($merged, array_flip(array_merge(array_keys($result), array_keys($added))));

    // Detect and process changes.
    foreach ($current as $key => $value) {
      if (isset($previous[$key]) && $previous[$key] !== $value) {

        // If we have an array, recurse.
        if (is_array($value) && is_array($previous[$key]) && isset($active[$key]) && is_array($active[$key])) {
          $recursion_keys = $parent_keys;
          $recursion_keys[] = $key;
          $level++;
          $result[$key] = self::mergeConfigItemStates($previous[$key], $value, $active[$key], $recursion_keys, $level);
        }
        else {
          $operation = static::OPERATION_IGNORE;

          // Accept the new value only if the item hasn't been customized.
          if (isset($active[$key]) && $active[$key] === $previous[$key]) {
            $result[$key] = $value;
            $operation = static::OPERATION_UPDATE;
          }
          self::$logs[$operation][] = [
            'name' => $key,
            'state' => [
              'active' => $active[$key],
              'previous' => $previous[$key],
              'new' => $value,
            ],
            'parents' => $parent_keys,
          ];
        }
      }
    }
  }
  else {
    $operation = static::OPERATION_IGNORE;

    // If the data is unchanged, use the current value. Otherwise, retain any
    // customization by keeping with the active value set above.
    if ($previous === $active) {
      $result = $current;
      $operation = static::OPERATION_SUBSTITUTE;
    }
    self::$logs[$operation][] = [
      'name' => end($parent_keys),
      'state' => [
        'active' => $active,
        'previous' => $previous,
        'new' => $current,
      ],
      'parents' => $parent_keys,
    ];
  }
  return $result;
}