From 2dcf32b8f1433dfd0d282077272489ef8cb59764 Mon Sep 17 00:00:00 2001
From: Fabian Bircher <f.bircher@gmail.com>
Date: Sun, 29 Aug 2021 23:17:17 +0200
Subject: [PATCH 1/8] Issue #3230397 by bircher: Normalize the way core does

---
 config_normalizer.info.yml                    |   5 +-
 config_normalizer.services.yml                |   7 +
 src/Annotation/ConfigNormalizer.php           |   3 +
 src/Config/NormalizedReadOnlyStorage.php      |  51 ++--
 .../NormalizedReadOnlyStorageInterface.php    |  25 +-
 src/Config/NormalizedStorageComparerTrait.php |  92 ++++----
 src/ConfigItemNormalizer.php                  | 217 ++++++++++++++----
 src/ConfigItemNormalizerInterface.php         |   2 +-
 .../ConfigNormalizerActive.php                |   4 +
 .../ConfigNormalizerFilterFormat.php          |   4 +-
 .../ConfigNormalizer/ConfigNormalizerSort.php |   3 +
 src/Plugin/ConfigNormalizerBase.php           |   3 +
 src/Plugin/ConfigNormalizerInterface.php      |   3 +
 src/Plugin/ConfigNormalizerManager.php        |   3 +
 14 files changed, 292 insertions(+), 130 deletions(-)

diff --git a/config_normalizer.info.yml b/config_normalizer.info.yml
index 4c5dc70..795c617 100644
--- a/config_normalizer.info.yml
+++ b/config_normalizer.info.yml
@@ -1,8 +1,5 @@
 name: 'Configuration Normalizer'
 type: module
 description: 'Normalizes configuration for comparison.'
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^8.8 || ^9
 package: Configuration
-dependencies:
-  - config_filter:config_filter
diff --git a/config_normalizer.services.yml b/config_normalizer.services.yml
index dca7b34..4267eeb 100644
--- a/config_normalizer.services.yml
+++ b/config_normalizer.services.yml
@@ -1,4 +1,11 @@
 services:
+
+  config_normalizer.normalizer:
+    class: Drupal\config_normalizer\ConfigItemNormalizer
+    arguments:
+      - "@config.typed"
+
+  # deprecated
   plugin.manager.config_normalizer:
     class: Drupal\config_normalizer\Plugin\ConfigNormalizerManager
     parent: default_plugin_manager
diff --git a/src/Annotation/ConfigNormalizer.php b/src/Annotation/ConfigNormalizer.php
index 4dff6dc..ef50a2c 100644
--- a/src/Annotation/ConfigNormalizer.php
+++ b/src/Annotation/ConfigNormalizer.php
@@ -10,6 +10,9 @@ use Drupal\Component\Annotation\Plugin;
  * @see \Drupal\config_normalizer\Plugin\ConfigNormalizerManager
  * @see plugin_api
  *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
+ *
  * @Annotation
  */
 class ConfigNormalizer extends Plugin {
diff --git a/src/Config/NormalizedReadOnlyStorage.php b/src/Config/NormalizedReadOnlyStorage.php
index a39a3b0..3812040 100644
--- a/src/Config/NormalizedReadOnlyStorage.php
+++ b/src/Config/NormalizedReadOnlyStorage.php
@@ -2,9 +2,8 @@
 
 namespace Drupal\config_normalizer\Config;
 
-use Drupal\config_filter\Config\ReadOnlyStorage;
-use Drupal\config_normalizer\ConfigItemNormalizer;
-use Drupal\config_normalizer\Plugin\ConfigNormalizerManager;
+use Drupal\config_normalizer\ConfigItemNormalizerInterface;
+use Drupal\Core\Config\ReadOnlyStorage;
 use Drupal\Core\Config\StorageInterface;
 
 /**
@@ -12,59 +11,42 @@ use Drupal\Core\Config\StorageInterface;
  */
 class NormalizedReadOnlyStorage extends ReadOnlyStorage implements NormalizedReadOnlyStorageInterface {
 
-  /**
-   * The config normalizer manager.
-   *
-   * @var \Drupal\config_normalizer\Plugin\ConfigNormalizerManager
-   */
-  protected $normalizerManager;
-
   /**
    * The config item normalizer.
    *
-   * @var \Drupal\config_normalizer\ConfigItemNormalizer
-   */
-  protected $configItemNormalizer;
-
-  /**
-   * An array of key-value pairs to pass additional context when needed.
-   *
-   * @var array
+   * @var \Drupal\config_normalizer\ConfigItemNormalizerInterface
    */
-  protected $context;
+  protected $normalizer;
 
   /**
    * Create a NormalizedReadOnlyStorage decorating another storage.
    *
    * @param \Drupal\Core\Config\StorageInterface $storage
    *   The decorated storage.
-   * @param \Drupal\config_normalizer\Plugin\ConfigNormalizerManager $normalizer_manager
-   *   The normalization manager.
+   * @param \Drupal\config_normalizer\ConfigItemNormalizerInterface|mixed $normalizer
+   *   The normalization manager. In 2.0.0 we will add a typehint.
    * @param array $context
-   *   (optional) An array of key-value pairs to pass additional context when
-   *   needed.
+   *   (optional, deprecated) This parameter will be removed in 2.0.0.
    */
-  public function __construct(StorageInterface $storage, ConfigNormalizerManager $normalizer_manager, array $context = []) {
+  public function __construct(StorageInterface $storage, $normalizer, array $context = []) {
     parent::__construct($storage);
-    $this->normalizerManager = $normalizer_manager;
-    $this->configItemNormalizer = new ConfigItemNormalizer($normalizer_manager, $context);
-    $this->setContext($context);
+    if (!$normalizer instanceof ConfigItemNormalizerInterface) {
+      $normalizer = \Drupal::service('config_normalizer.normalizer');
+    }
+    $this->normalizer = $normalizer;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getContext() {
-    return $this->context;
+    return [];
   }
 
   /**
    * {@inheritdoc}
    */
   public function setContext(array $context = []) {
-    $context += NormalizedReadOnlyStorageInterface::DEFAULT_CONTEXT;
-
-    $this->context = $context;
   }
 
   /**
@@ -97,8 +79,7 @@ class NormalizedReadOnlyStorage extends ReadOnlyStorage implements NormalizedRea
   public function createCollection($collection) {
     return new static(
       $this->storage->createCollection($collection),
-      $this->normalizerManager,
-      $this->context
+      $this->normalizer
     );
   }
 
@@ -107,7 +88,7 @@ class NormalizedReadOnlyStorage extends ReadOnlyStorage implements NormalizedRea
    *
    * @param string $name
    *   The name of a configuration object to load.
-   * @param array $data
+   * @param array|bool $data
    *   The configuration data to normalize.
    *
    * @return array|bool
@@ -116,7 +97,7 @@ class NormalizedReadOnlyStorage extends ReadOnlyStorage implements NormalizedRea
    */
   protected function normalize($name, $data) {
     if (!is_bool($data)) {
-      $data = $this->configItemNormalizer->normalize($name, $data, $this->context);
+      $data = $this->normalizer->normalize($name, $data);
     }
 
     return $data;
diff --git a/src/Config/NormalizedReadOnlyStorageInterface.php b/src/Config/NormalizedReadOnlyStorageInterface.php
index b38045b..7d8c5f0 100644
--- a/src/Config/NormalizedReadOnlyStorageInterface.php
+++ b/src/Config/NormalizedReadOnlyStorageInterface.php
@@ -2,9 +2,6 @@
 
 namespace Drupal\config_normalizer\Config;
 
-use Drupal\config_filter\Config\ReadOnlyStorage;
-use Drupal\config_normalizer\ConfigItemNormalizer;
-use Drupal\config_normalizer\Plugin\ConfigNormalizerManager;
 use Drupal\Core\Config\StorageInterface;
 
 /**
@@ -20,6 +17,9 @@ interface NormalizedReadOnlyStorageInterface extends StorageInterface {
    *
    * This mode is typically used on both storages that are passed to a
    * class implementing \Drupal\Core\Config\StorageComparerInterface.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   const NORMALIZATION_MODE_COMPARE = 'compare';
 
@@ -30,11 +30,17 @@ interface NormalizedReadOnlyStorageInterface extends StorageInterface {
    * written, only write-appropriate normalization should be done. For example,
    * data should not be sorted since sorting will leave data in a state that
    * may not be appropriate for writing.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   const NORMALIZATION_MODE_PROVIDE = 'provide';
 
   /**
    * The default normalization mode.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   const DEFAULT_NORMALIZATION_MODE = self::NORMALIZATION_MODE_COMPARE;
 
@@ -47,6 +53,9 @@ interface NormalizedReadOnlyStorageInterface extends StorageInterface {
    *   against. When this is the site's active configuration storage,
    *   config.storage, normalization should replicate any changes made at
    *   config install time.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   const DEFAULT_CONTEXT = [
     'normalization_mode' => self::DEFAULT_NORMALIZATION_MODE,
@@ -56,8 +65,11 @@ interface NormalizedReadOnlyStorageInterface extends StorageInterface {
   /**
    * Gets the context to be used for normalization.
    *
-   * @param array
+   * @return array
    *   An array of key-value pairs to pass additional context when needed.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   public function getContext();
 
@@ -67,7 +79,10 @@ interface NormalizedReadOnlyStorageInterface extends StorageInterface {
    * If not given, values are defaulted to those in ::DEFAULT_CONTEXT.
    *
    * @param array $context
-   *   (optional) An array of key-value pairs to pass additional context when needed.
+   *   (optional) Unused.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   public function setContext(array $context = []);
 
diff --git a/src/Config/NormalizedStorageComparerTrait.php b/src/Config/NormalizedStorageComparerTrait.php
index 6e9ceb7..3cb65f3 100644
--- a/src/Config/NormalizedStorageComparerTrait.php
+++ b/src/Config/NormalizedStorageComparerTrait.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\config_normalizer\Config;
 
+use Drupal\config_normalizer\ConfigItemNormalizerInterface;
 use Drupal\config_normalizer\Plugin\ConfigNormalizerManager;
 use Drupal\Core\Config\ConfigManagerInterface;
 use Drupal\Core\Config\StorageComparer;
@@ -11,25 +12,16 @@ use Drupal\Core\Config\StorageInterface;
  * Using this trait will add a ::createStorageComparer() method to the class.
  *
  * If the class is capable of injecting services from the container, it should
- * inject the 'config.manager' service by calling $this->setConfigManager() and
- * the 'plugin.manager.config_normalizer' service by calling
- * $this->setNormalizerManager().
+ * inject the 'config_normalizer.normalizer' service and call setNormalizer()
  */
 trait NormalizedStorageComparerTrait {
 
   /**
-   * The normalizer plugin manager.
+   * The config normalizer service.
    *
-   * @var \Drupal\config_normalizer\Plugin\ConfigNormalizerManager
+   * @var \Drupal\config_normalizer\ConfigItemNormalizerInterface
    */
-  protected $normalizerManager;
-
-  /**
-   * The configuration manager.
-   *
-   * @var \Drupal\Core\Config\ConfigManagerInterface
-   */
-  protected $configManager;
+  protected $configNormalizer;
 
   /**
    * Creates and returns a storage comparer.
@@ -39,43 +31,61 @@ trait NormalizedStorageComparerTrait {
    * @param \Drupal\Core\Config\StorageInterface $target_storage
    *   The target storage.
    * @param string $mode
-   *   (optional) The normalization mode.
+   *   (optional, deprecated) The normalization mode.
    *
-   * @return \Drupal\Core\Config\StorageComparer;
+   * @return \Drupal\Core\Config\StorageComparer
    *   A storage comparer.
    */
-  protected function createStorageComparer(StorageInterface $source_storage, StorageInterface $target_storage, $mode = NormalizedReadOnlyStorageInterface::DEFAULT_NORMALIZATION_MODE) {
-    $source_context = [
-      'normalization_mode' => $mode,
-      'reference_storage_service' => $target_storage,
-    ];
-
-    $target_context = [
-      'normalization_mode' => $mode,
-      'reference_storage_service' => $source_storage,
-    ];
-
+  protected function createStorageComparer(StorageInterface $source_storage, StorageInterface $target_storage, $mode = NULL) {
     // Set up a storage comparer using normalized storages.
     $storage_comparer = new StorageComparer(
-      new NormalizedReadOnlyStorage($source_storage, $this->getNormalizerManager(), $source_context),
-      new NormalizedReadOnlyStorage($target_storage, $this->getNormalizerManager(), $target_context),
-      $this->getConfigManager()
+      new NormalizedReadOnlyStorage($source_storage, $this->getNormalizer()),
+      new NormalizedReadOnlyStorage($target_storage, $this->getNormalizer())
     );
 
     return $storage_comparer;
   }
 
+  /**
+   * Gets the normalizer service.
+   *
+   * @return \Drupal\config_normalizer\ConfigItemNormalizerInterface
+   *   The configuration normalizer.
+   */
+  protected function getNormalizer() {
+    if (!$this->configNormalizer) {
+      $this->configNormalizer = \Drupal::service('config_normalizer.normalizer');
+    }
+    return $this->configNormalizer;
+  }
+
+  /**
+   * Sets the normalizer manager service to use.
+   *
+   * @param \Drupal\config_normalizer\ConfigItemNormalizerInterface $normalizer
+   *   The normalizer service.
+   *
+   * @return $this
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
+   */
+  public function setNormalizer(ConfigItemNormalizerInterface $normalizer) {
+    $this->configNormalizer = $normalizer;
+    return $this;
+  }
+
   /**
    * Gets the configuration manager service.
    *
    * @return \Drupal\Core\Config\ConfigManagerInterface
    *   The configuration manager.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   protected function getConfigManager() {
-    if (!$this->configManager) {
-      $this->configManager = \Drupal::service('config.manager');
-    }
-    return $this->configManager;
+    return \Drupal::service('config.manager');
   }
 
   /**
@@ -85,9 +95,11 @@ trait NormalizedStorageComparerTrait {
    *   The configuration manager service.
    *
    * @return $this
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   public function setConfigManager(ConfigManagerInterface $config_manager) {
-    $this->configManager = $config_manager;
     return $this;
   }
 
@@ -96,12 +108,12 @@ trait NormalizedStorageComparerTrait {
    *
    * @return \Drupal\config_normalizer\Plugin\ConfigNormalizerManager
    *   The normalizer manager.
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   protected function getNormalizerManager() {
-    if (!$this->normalizerManager) {
-      $this->normalizerManager = \Drupal::service('plugin.manager.config_normalizer');
-    }
-    return $this->normalizerManager;
+    return \Drupal::service('plugin.manager.config_normalizer');
   }
 
   /**
@@ -111,9 +123,11 @@ trait NormalizedStorageComparerTrait {
    *   The normalizer manager service.
    *
    * @return $this
+   *
+   * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+   * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
   public function setNormalizerManager(ConfigNormalizerManager $normalizer_manager) {
-    $this->normalizerManager = $normalizer_manager;
     return $this;
   }
 
diff --git a/src/ConfigItemNormalizer.php b/src/ConfigItemNormalizer.php
index 8c3fa89..856de38 100644
--- a/src/ConfigItemNormalizer.php
+++ b/src/ConfigItemNormalizer.php
@@ -2,7 +2,17 @@
 
 namespace Drupal\config_normalizer;
 
-use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\Schema\Ignore;
+use Drupal\Core\Config\Schema\Mapping;
+use Drupal\Core\Config\Schema\Sequence;
+use Drupal\Core\Config\Schema\SequenceDataDefinition;
+use Drupal\Core\Config\Schema\Undefined;
+use Drupal\Core\Config\StorableConfigBase;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\Config\UnsupportedDataTypeConfigException;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
 
 /**
  * Class responsible for performing configuration normalization.
@@ -10,67 +20,184 @@ use Drupal\Component\Plugin\PluginManagerInterface;
 class ConfigItemNormalizer implements ConfigItemNormalizerInterface {
 
   /**
-   * The configuration normalizer plugin manager.
+   * The typed config manager to get the schema from.
    *
-   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   * @var \Drupal\Core\Config\TypedConfigManagerInterface
    */
-  protected $normalizerManager;
+  protected $typedConfigManager;
 
   /**
-   * Local cache for normalizer instances.
+   * ConfigCaster constructor.
    *
-   * @var array
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
+   *   The typed config manager to look up the schema.
    */
-  protected $normalizers;
-
-  /**
-   * Constructs a new ConfigItemNormalizer object.
-   *
-   * @param \Drupal\Component\Plugin\PluginManagerInterface $normalizer_manager
-   *   The configuration normalizer plugin manager.
-   */
-  public function __construct(PluginManagerInterface $normalizer_manager) {
-    $this->normalizerManager = $normalizer_manager;
+  public function __construct(TypedConfigManagerInterface $typedConfigManager) {
+    $this->typedConfigManager = $typedConfigManager;
   }
 
   /**
    * {@inheritdoc}
    */
   public function normalize($name, array $data, array $context = []) {
-    $normalizers = $this->normalizerManager->getDefinitions();
-    uasort($normalizers, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
+    // The sorter is an anonymous class extending from StorableConfigBase.
+    // We need to do this because the logic for sorting is in a class meant
+    // for config objects and not for services.
+    $sorter = new class($this->typedConfigManager) extends StorableConfigBase {
 
-    foreach (array_keys($normalizers) as $id) {
-      $this->applyNormalizer($id, $name, $data, $context);
-    }
+      /**
+       * Sort the config.
+       *
+       * This method is named to make it unlikely that it is overriding a core
+       * method.
+       *
+       * @param string $name
+       *   The config name.
+       * @param array $data
+       *   The data.
+       *
+       * @return array
+       *   The sorted array.
+       */
+      public function anonymousSort(string $name, array $data): array {
+        // Set the object up.
+        self::validateName($name);
+        $this->validateKeys($data);
+        $this->setName($name)->initWithData($data);
 
-    return $data;
-  }
+        // This is essentially what \Drupal\Core\Config\Config::save does when
+        // there is untrusted data before persisting it and dispatching events.
+        if ($this->typedConfigManager->hasConfigSchema($this->name)) {
+          // We use the patched version of the method.
+          $this->data = $this->castValue2852557(NULL, $this->data);
+        }
+        else {
+          foreach ($this->data as $key => $value) {
+            $this->validateValue($key, $value);
+          }
+        }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function applyNormalizer($id, $name, array &$data, array $context) {
-    $normalizer = $this->getNormalizerInstance($id);
-    $normalizer->normalize($name, $data, $context);
-  }
+        // That should be it.
+        return $this->data;
+      }
 
-  /**
-   * Returns an instance of the specified package generation normalizer.
-   *
-   * @param string $id
-   *   The string identifier of the normalizer to use to package
-   *   configuration.
-   *
-   * @return \Drupal\config_normalizer\Plugin\ConfigNormalizerInterface
-   */
-  protected function getNormalizerInstance($id) {
-    if (!isset($this->normalizers[$id])) {
-      $instance = $this->normalizerManager->createInstance($id);
-      $this->normalizers[$id] = $instance;
-    }
+      /**
+       * Casts the value to correct data type using the configuration schema.
+       *
+       * This is the patched version from
+       * https://www.drupal.org/project/drupal/issues/2852557
+       *
+       * @param string|null $key
+       *   A string that maps to a key within the configuration data. If NULL
+       *   the top level mapping will be processed.
+       * @param mixed $value
+       *   Value to associate with the key.
+       *
+       * @return mixed
+       *   The value cast to the type indicated in the schema.
+       *
+       * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
+       *   If the value is unsupported in configuration.
+       */
+      protected function castValue2852557($key, $value) {
+        $element = $this->getSchemaWrapper();
+        if ($key !== NULL) {
+          $element = $element->get($key);
+        }
+
+        // Do not cast value if it is unknown or defined to be ignored.
+        if ($element && ($element instanceof Undefined || $element instanceof Ignore)) {
+          $this->validateValue($key, $value);
+          return $value;
+        }
+        if (is_scalar($value) || $value === NULL) {
+          if ($element && $element instanceof PrimitiveInterface) {
+            $empty_value = $value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface);
+
+            if ($value === NULL || $empty_value) {
+              $value = NULL;
+            }
+            else {
+              $value = $element->getCastedValue();
+            }
+          }
+        }
+        else {
+          // Throw exception on any non-scalar or non-array value.
+          if (!is_array($value)) {
+            throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
+          }
+          // Recurse into any nested keys.
+          foreach ($value as $nested_value_key => $nested_value) {
+            $lookup_key = $key ? $key . '.' . $nested_value_key : $nested_value_key;
+            $value[$nested_value_key] = $this->castValue2852557($lookup_key, $nested_value);
+          }
+
+          // Only sort maps when we have more than 1 element to sort.
+          if ($element instanceof Mapping && count($value) > 1) {
+            $mapping = $element->getDataDefinition()['mapping'];
+            if (is_array($mapping)) {
+              // Only sort the keys in $value.
+              $mapping = array_intersect_key($mapping, $value);
+              // Sort the array in $value using the mapping definition.
+              $value = array_replace($mapping, $value);
+            }
+          }
+
+          if ($element instanceof Sequence) {
+            $data_definition = $element->getDataDefinition();
+            if ($data_definition instanceof SequenceDataDefinition) {
+              // Apply any sorting defined on the schema.
+              switch ($data_definition->getOrderBy()) {
+                case 'key':
+                  ksort($value);
+                  break;
+
+                case 'value':
+                  // The PHP documentation notes that "Be careful when sorting
+                  // arrays with mixed types values because sort() can produce
+                  // unpredictable results". There is no risk here because
+                  // \Drupal\Core\Config\StorableConfigBase::castValue() has
+                  // already cast all values to the same type using the
+                  // configuration schema.
+                  sort($value);
+                  break;
+
+              }
+            }
+          }
+        }
+        return $value;
+      }
+
+      /**
+       * The constructor for passing the TypedConfigManager.
+       *
+       * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
+       *   The taped config manager.
+       */
+      public function __construct(TypedConfigManagerInterface $typedConfigManager) {
+        $this->typedConfigManager = $typedConfigManager;
+      }
+
+      /**
+       * {@inheritdoc}
+       */
+      public function save($has_trusted_data = FALSE) {
+        throw new \LogicException();
+      }
+
+      /**
+       * {@inheritdoc}
+       */
+      public function delete() {
+        throw new \LogicException();
+      }
+
+    };
 
-    return $this->normalizers[$id];
+    // Sort the data using the core class we extended.
+    return $sorter->anonymousSort($name, $data);
   }
 
 }
diff --git a/src/ConfigItemNormalizerInterface.php b/src/ConfigItemNormalizerInterface.php
index 45f3095..2e1116b 100644
--- a/src/ConfigItemNormalizerInterface.php
+++ b/src/ConfigItemNormalizerInterface.php
@@ -18,7 +18,7 @@ interface ConfigItemNormalizerInterface {
    * @param array $data
    *   Configuration array to normalize.
    * @param array $context
-   *   (optional) An array of key-value pairs to pass additional context when needed.
+   *   (optional, deprecated) This parameter will be removed in 2.0.0.
    *
    * @return array
    *   Normalized configuration array.
diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php
index 09d9ef8..e79beea 100644
--- a/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php
+++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php
@@ -14,6 +14,9 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  *   weight = 0,
  *   description = @Translation("Copies over properties that are set by core when configuration is saved to the active storage."),
  * )
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerActive extends ConfigNormalizerBase implements ContainerFactoryPluginInterface {
 
@@ -32,4 +35,5 @@ class ConfigNormalizerActive extends ConfigNormalizerBase implements ContainerFa
       $data = array_intersect_key($merged, array_flip(array_merge(array_keys($data), ['uuid', '_core'])));
     }
   }
+
 }
diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php
index 3201a0c..9558f50 100644
--- a/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php
+++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php
@@ -3,7 +3,6 @@
 namespace Drupal\config_normalizer\Plugin\ConfigNormalizer;
 
 use Drupal\config_normalizer\Plugin\ConfigNormalizerBase;
-use Drupal\Component\Plugin\DependentPluginInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 
 /**
@@ -15,6 +14,9 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  *   weight = 20,
  *   description = @Translation("Removes the roles element from filter formats, since this element is valid only on exported configuration."),
  * )
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerFilterFormat extends ConfigNormalizerBase implements ContainerFactoryPluginInterface {
 
diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php
index e627454..c1cf970 100644
--- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php
+++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php
@@ -15,6 +15,9 @@ use Symfony\Component\Yaml\Inline;
  *   weight = 20,
  *   description = @Translation("Recursively sorts a configuration array."),
  * )
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFactoryPluginInterface {
 
diff --git a/src/Plugin/ConfigNormalizerBase.php b/src/Plugin/ConfigNormalizerBase.php
index 1d7eb28..6adecb3 100644
--- a/src/Plugin/ConfigNormalizerBase.php
+++ b/src/Plugin/ConfigNormalizerBase.php
@@ -9,6 +9,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Base class for Config normalizer plugins.
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 abstract class ConfigNormalizerBase extends PluginBase implements ConfigNormalizerInterface {
 
diff --git a/src/Plugin/ConfigNormalizerInterface.php b/src/Plugin/ConfigNormalizerInterface.php
index 608673e..7ecc549 100644
--- a/src/Plugin/ConfigNormalizerInterface.php
+++ b/src/Plugin/ConfigNormalizerInterface.php
@@ -6,6 +6,9 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
 
 /**
  * Defines an interface for Config normalizer plugins.
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 interface ConfigNormalizerInterface extends PluginInspectionInterface {
 
diff --git a/src/Plugin/ConfigNormalizerManager.php b/src/Plugin/ConfigNormalizerManager.php
index 725784e..b81a2a1 100644
--- a/src/Plugin/ConfigNormalizerManager.php
+++ b/src/Plugin/ConfigNormalizerManager.php
@@ -9,6 +9,9 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 
 /**
  * Provides the Config normalizer plugin manager.
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerManager extends DefaultPluginManager {
 
-- 
GitLab


From 53ec796ed0b43bbce0c1d3fa6adf7fde641d9520 Mon Sep 17 00:00:00 2001
From: Fabian Bircher <f.bircher@gmail.com>
Date: Mon, 30 Aug 2021 09:23:15 +0200
Subject: [PATCH 2/8] Issue #3230426 by bircher: Rename
 ConfigItemNormalizerInterface to ConfigNormalizerInterface as main API of the
 module

---
 config_normalizer.services.yml                |   2 +-
 src/Config/NormalizedReadOnlyStorage.php      |   8 +-
 src/Config/NormalizedStorageComparerTrait.php |  10 +-
 src/ConfigItemNormalizer.php                  | 193 +----------------
 src/ConfigItemNormalizerInterface.php         |   5 +-
 src/ConfigNormalizer.php                      | 203 ++++++++++++++++++
 src/ConfigNormalizerInterface.php             |  28 +++
 7 files changed, 250 insertions(+), 199 deletions(-)
 create mode 100644 src/ConfigNormalizer.php
 create mode 100644 src/ConfigNormalizerInterface.php

diff --git a/config_normalizer.services.yml b/config_normalizer.services.yml
index 4267eeb..d7bfc5d 100644
--- a/config_normalizer.services.yml
+++ b/config_normalizer.services.yml
@@ -1,7 +1,7 @@
 services:
 
   config_normalizer.normalizer:
-    class: Drupal\config_normalizer\ConfigItemNormalizer
+    class: Drupal\config_normalizer\ConfigNormalizer
     arguments:
       - "@config.typed"
 
diff --git a/src/Config/NormalizedReadOnlyStorage.php b/src/Config/NormalizedReadOnlyStorage.php
index 3812040..96595a7 100644
--- a/src/Config/NormalizedReadOnlyStorage.php
+++ b/src/Config/NormalizedReadOnlyStorage.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\config_normalizer\Config;
 
-use Drupal\config_normalizer\ConfigItemNormalizerInterface;
+use Drupal\config_normalizer\ConfigNormalizerInterface;
 use Drupal\Core\Config\ReadOnlyStorage;
 use Drupal\Core\Config\StorageInterface;
 
@@ -14,7 +14,7 @@ class NormalizedReadOnlyStorage extends ReadOnlyStorage implements NormalizedRea
   /**
    * The config item normalizer.
    *
-   * @var \Drupal\config_normalizer\ConfigItemNormalizerInterface
+   * @var \Drupal\config_normalizer\ConfigNormalizerInterface
    */
   protected $normalizer;
 
@@ -23,14 +23,14 @@ class NormalizedReadOnlyStorage extends ReadOnlyStorage implements NormalizedRea
    *
    * @param \Drupal\Core\Config\StorageInterface $storage
    *   The decorated storage.
-   * @param \Drupal\config_normalizer\ConfigItemNormalizerInterface|mixed $normalizer
+   * @param \Drupal\config_normalizer\ConfigNormalizerInterface|mixed $normalizer
    *   The normalization manager. In 2.0.0 we will add a typehint.
    * @param array $context
    *   (optional, deprecated) This parameter will be removed in 2.0.0.
    */
   public function __construct(StorageInterface $storage, $normalizer, array $context = []) {
     parent::__construct($storage);
-    if (!$normalizer instanceof ConfigItemNormalizerInterface) {
+    if (!$normalizer instanceof ConfigNormalizerInterface) {
       $normalizer = \Drupal::service('config_normalizer.normalizer');
     }
     $this->normalizer = $normalizer;
diff --git a/src/Config/NormalizedStorageComparerTrait.php b/src/Config/NormalizedStorageComparerTrait.php
index 3cb65f3..f72204c 100644
--- a/src/Config/NormalizedStorageComparerTrait.php
+++ b/src/Config/NormalizedStorageComparerTrait.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\config_normalizer\Config;
 
-use Drupal\config_normalizer\ConfigItemNormalizerInterface;
+use Drupal\config_normalizer\ConfigNormalizerInterface;
 use Drupal\config_normalizer\Plugin\ConfigNormalizerManager;
 use Drupal\Core\Config\ConfigManagerInterface;
 use Drupal\Core\Config\StorageComparer;
@@ -19,7 +19,7 @@ trait NormalizedStorageComparerTrait {
   /**
    * The config normalizer service.
    *
-   * @var \Drupal\config_normalizer\ConfigItemNormalizerInterface
+   * @var \Drupal\config_normalizer\ConfigNormalizerInterface
    */
   protected $configNormalizer;
 
@@ -49,7 +49,7 @@ trait NormalizedStorageComparerTrait {
   /**
    * Gets the normalizer service.
    *
-   * @return \Drupal\config_normalizer\ConfigItemNormalizerInterface
+   * @return \Drupal\config_normalizer\ConfigNormalizerInterface
    *   The configuration normalizer.
    */
   protected function getNormalizer() {
@@ -62,7 +62,7 @@ trait NormalizedStorageComparerTrait {
   /**
    * Sets the normalizer manager service to use.
    *
-   * @param \Drupal\config_normalizer\ConfigItemNormalizerInterface $normalizer
+   * @param \Drupal\config_normalizer\ConfigNormalizerInterface $normalizer
    *   The normalizer service.
    *
    * @return $this
@@ -70,7 +70,7 @@ trait NormalizedStorageComparerTrait {
    * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
    * @see https://www.drupal.org/project/config_normalizer/issues/3230398
    */
-  public function setNormalizer(ConfigItemNormalizerInterface $normalizer) {
+  public function setNormalizer(ConfigNormalizerInterface $normalizer) {
     $this->configNormalizer = $normalizer;
     return $this;
   }
diff --git a/src/ConfigItemNormalizer.php b/src/ConfigItemNormalizer.php
index 856de38..a6b95c4 100644
--- a/src/ConfigItemNormalizer.php
+++ b/src/ConfigItemNormalizer.php
@@ -2,202 +2,19 @@
 
 namespace Drupal\config_normalizer;
 
-use Drupal\Core\Config\Schema\Ignore;
-use Drupal\Core\Config\Schema\Mapping;
-use Drupal\Core\Config\Schema\Sequence;
-use Drupal\Core\Config\Schema\SequenceDataDefinition;
-use Drupal\Core\Config\Schema\Undefined;
-use Drupal\Core\Config\StorableConfigBase;
-use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\Config\UnsupportedDataTypeConfigException;
-use Drupal\Core\TypedData\PrimitiveInterface;
-use Drupal\Core\TypedData\Type\FloatInterface;
-use Drupal\Core\TypedData\Type\IntegerInterface;
-
 /**
  * Class responsible for performing configuration normalization.
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha2 and is removed from config_normalizer:2.0.0. Use ConfigNormalizer instead.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230426
  */
-class ConfigItemNormalizer implements ConfigItemNormalizerInterface {
-
-  /**
-   * The typed config manager to get the schema from.
-   *
-   * @var \Drupal\Core\Config\TypedConfigManagerInterface
-   */
-  protected $typedConfigManager;
-
-  /**
-   * ConfigCaster constructor.
-   *
-   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
-   *   The typed config manager to look up the schema.
-   */
-  public function __construct(TypedConfigManagerInterface $typedConfigManager) {
-    $this->typedConfigManager = $typedConfigManager;
-  }
+class ConfigItemNormalizer extends ConfigNormalizer implements ConfigItemNormalizerInterface {
 
   /**
    * {@inheritdoc}
    */
   public function normalize($name, array $data, array $context = []) {
-    // The sorter is an anonymous class extending from StorableConfigBase.
-    // We need to do this because the logic for sorting is in a class meant
-    // for config objects and not for services.
-    $sorter = new class($this->typedConfigManager) extends StorableConfigBase {
-
-      /**
-       * Sort the config.
-       *
-       * This method is named to make it unlikely that it is overriding a core
-       * method.
-       *
-       * @param string $name
-       *   The config name.
-       * @param array $data
-       *   The data.
-       *
-       * @return array
-       *   The sorted array.
-       */
-      public function anonymousSort(string $name, array $data): array {
-        // Set the object up.
-        self::validateName($name);
-        $this->validateKeys($data);
-        $this->setName($name)->initWithData($data);
-
-        // This is essentially what \Drupal\Core\Config\Config::save does when
-        // there is untrusted data before persisting it and dispatching events.
-        if ($this->typedConfigManager->hasConfigSchema($this->name)) {
-          // We use the patched version of the method.
-          $this->data = $this->castValue2852557(NULL, $this->data);
-        }
-        else {
-          foreach ($this->data as $key => $value) {
-            $this->validateValue($key, $value);
-          }
-        }
-
-        // That should be it.
-        return $this->data;
-      }
-
-      /**
-       * Casts the value to correct data type using the configuration schema.
-       *
-       * This is the patched version from
-       * https://www.drupal.org/project/drupal/issues/2852557
-       *
-       * @param string|null $key
-       *   A string that maps to a key within the configuration data. If NULL
-       *   the top level mapping will be processed.
-       * @param mixed $value
-       *   Value to associate with the key.
-       *
-       * @return mixed
-       *   The value cast to the type indicated in the schema.
-       *
-       * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
-       *   If the value is unsupported in configuration.
-       */
-      protected function castValue2852557($key, $value) {
-        $element = $this->getSchemaWrapper();
-        if ($key !== NULL) {
-          $element = $element->get($key);
-        }
-
-        // Do not cast value if it is unknown or defined to be ignored.
-        if ($element && ($element instanceof Undefined || $element instanceof Ignore)) {
-          $this->validateValue($key, $value);
-          return $value;
-        }
-        if (is_scalar($value) || $value === NULL) {
-          if ($element && $element instanceof PrimitiveInterface) {
-            $empty_value = $value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface);
-
-            if ($value === NULL || $empty_value) {
-              $value = NULL;
-            }
-            else {
-              $value = $element->getCastedValue();
-            }
-          }
-        }
-        else {
-          // Throw exception on any non-scalar or non-array value.
-          if (!is_array($value)) {
-            throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
-          }
-          // Recurse into any nested keys.
-          foreach ($value as $nested_value_key => $nested_value) {
-            $lookup_key = $key ? $key . '.' . $nested_value_key : $nested_value_key;
-            $value[$nested_value_key] = $this->castValue2852557($lookup_key, $nested_value);
-          }
-
-          // Only sort maps when we have more than 1 element to sort.
-          if ($element instanceof Mapping && count($value) > 1) {
-            $mapping = $element->getDataDefinition()['mapping'];
-            if (is_array($mapping)) {
-              // Only sort the keys in $value.
-              $mapping = array_intersect_key($mapping, $value);
-              // Sort the array in $value using the mapping definition.
-              $value = array_replace($mapping, $value);
-            }
-          }
-
-          if ($element instanceof Sequence) {
-            $data_definition = $element->getDataDefinition();
-            if ($data_definition instanceof SequenceDataDefinition) {
-              // Apply any sorting defined on the schema.
-              switch ($data_definition->getOrderBy()) {
-                case 'key':
-                  ksort($value);
-                  break;
-
-                case 'value':
-                  // The PHP documentation notes that "Be careful when sorting
-                  // arrays with mixed types values because sort() can produce
-                  // unpredictable results". There is no risk here because
-                  // \Drupal\Core\Config\StorableConfigBase::castValue() has
-                  // already cast all values to the same type using the
-                  // configuration schema.
-                  sort($value);
-                  break;
-
-              }
-            }
-          }
-        }
-        return $value;
-      }
-
-      /**
-       * The constructor for passing the TypedConfigManager.
-       *
-       * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
-       *   The taped config manager.
-       */
-      public function __construct(TypedConfigManagerInterface $typedConfigManager) {
-        $this->typedConfigManager = $typedConfigManager;
-      }
-
-      /**
-       * {@inheritdoc}
-       */
-      public function save($has_trusted_data = FALSE) {
-        throw new \LogicException();
-      }
-
-      /**
-       * {@inheritdoc}
-       */
-      public function delete() {
-        throw new \LogicException();
-      }
-
-    };
-
-    // Sort the data using the core class we extended.
-    return $sorter->anonymousSort($name, $data);
+    return parent::normalize($name, $data);
   }
 
 }
diff --git a/src/ConfigItemNormalizerInterface.php b/src/ConfigItemNormalizerInterface.php
index 2e1116b..f3d95b5 100644
--- a/src/ConfigItemNormalizerInterface.php
+++ b/src/ConfigItemNormalizerInterface.php
@@ -4,8 +4,11 @@ namespace Drupal\config_normalizer;
 
 /**
  * Defines an interface for config item normalizers.
+ *
+ * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. Use ConfigNormalizerInterface instead.
+ * @see https://www.drupal.org/project/config_normalizer/issues/3230426
  */
-interface ConfigItemNormalizerInterface {
+interface ConfigItemNormalizerInterface extends ConfigNormalizerInterface {
 
   /**
    * Normalizes config for comparison.
diff --git a/src/ConfigNormalizer.php b/src/ConfigNormalizer.php
new file mode 100644
index 0000000..5ad4a27
--- /dev/null
+++ b/src/ConfigNormalizer.php
@@ -0,0 +1,203 @@
+<?php
+
+namespace Drupal\config_normalizer;
+
+use Drupal\Core\Config\Schema\Ignore;
+use Drupal\Core\Config\Schema\Mapping;
+use Drupal\Core\Config\Schema\Sequence;
+use Drupal\Core\Config\Schema\SequenceDataDefinition;
+use Drupal\Core\Config\Schema\Undefined;
+use Drupal\Core\Config\StorableConfigBase;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\Config\UnsupportedDataTypeConfigException;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
+
+/**
+ * Class responsible for performing configuration normalization.
+ */
+class ConfigNormalizer implements ConfigNormalizerInterface {
+
+  /**
+   * The typed config manager to get the schema from.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManagerInterface
+   */
+  protected $typedConfigManager;
+
+  /**
+   * ConfigCaster constructor.
+   *
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
+   *   The typed config manager to look up the schema.
+   */
+  public function __construct(TypedConfigManagerInterface $typedConfigManager) {
+    $this->typedConfigManager = $typedConfigManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($name, array $data) {
+    // The sorter is an anonymous class extending from StorableConfigBase.
+    // We need to do this because the logic for sorting is in a class meant
+    // for config objects and not for services.
+    $sorter = new class($this->typedConfigManager) extends StorableConfigBase {
+
+      /**
+       * Sort the config.
+       *
+       * This method is named to make it unlikely that it is overriding a core
+       * method.
+       *
+       * @param string $name
+       *   The config name.
+       * @param array $data
+       *   The data.
+       *
+       * @return array
+       *   The sorted array.
+       */
+      public function anonymousSort(string $name, array $data): array {
+        // Set the object up.
+        self::validateName($name);
+        $this->validateKeys($data);
+        $this->setName($name)->initWithData($data);
+
+        // This is essentially what \Drupal\Core\Config\Config::save does when
+        // there is untrusted data before persisting it and dispatching events.
+        if ($this->typedConfigManager->hasConfigSchema($this->name)) {
+          // We use the patched version of the method.
+          $this->data = $this->castValue2852557(NULL, $this->data);
+        }
+        else {
+          foreach ($this->data as $key => $value) {
+            $this->validateValue($key, $value);
+          }
+        }
+
+        // That should be it.
+        return $this->data;
+      }
+
+      /**
+       * Casts the value to correct data type using the configuration schema.
+       *
+       * This is the patched version from
+       * https://www.drupal.org/project/drupal/issues/2852557
+       *
+       * @param string|null $key
+       *   A string that maps to a key within the configuration data. If NULL
+       *   the top level mapping will be processed.
+       * @param mixed $value
+       *   Value to associate with the key.
+       *
+       * @return mixed
+       *   The value cast to the type indicated in the schema.
+       *
+       * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
+       *   If the value is unsupported in configuration.
+       */
+      protected function castValue2852557($key, $value) {
+        $element = $this->getSchemaWrapper();
+        if ($key !== NULL) {
+          $element = $element->get($key);
+        }
+
+        // Do not cast value if it is unknown or defined to be ignored.
+        if ($element && ($element instanceof Undefined || $element instanceof Ignore)) {
+          $this->validateValue($key, $value);
+          return $value;
+        }
+        if (is_scalar($value) || $value === NULL) {
+          if ($element && $element instanceof PrimitiveInterface) {
+            $empty_value = $value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface);
+
+            if ($value === NULL || $empty_value) {
+              $value = NULL;
+            }
+            else {
+              $value = $element->getCastedValue();
+            }
+          }
+        }
+        else {
+          // Throw exception on any non-scalar or non-array value.
+          if (!is_array($value)) {
+            throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
+          }
+          // Recurse into any nested keys.
+          foreach ($value as $nested_value_key => $nested_value) {
+            $lookup_key = $key ? $key . '.' . $nested_value_key : $nested_value_key;
+            $value[$nested_value_key] = $this->castValue2852557($lookup_key, $nested_value);
+          }
+
+          // Only sort maps when we have more than 1 element to sort.
+          if ($element instanceof Mapping && count($value) > 1) {
+            $mapping = $element->getDataDefinition()['mapping'];
+            if (is_array($mapping)) {
+              // Only sort the keys in $value.
+              $mapping = array_intersect_key($mapping, $value);
+              // Sort the array in $value using the mapping definition.
+              $value = array_replace($mapping, $value);
+            }
+          }
+
+          if ($element instanceof Sequence) {
+            $data_definition = $element->getDataDefinition();
+            if ($data_definition instanceof SequenceDataDefinition) {
+              // Apply any sorting defined on the schema.
+              switch ($data_definition->getOrderBy()) {
+                case 'key':
+                  ksort($value);
+                  break;
+
+                case 'value':
+                  // The PHP documentation notes that "Be careful when sorting
+                  // arrays with mixed types values because sort() can produce
+                  // unpredictable results". There is no risk here because
+                  // \Drupal\Core\Config\StorableConfigBase::castValue() has
+                  // already cast all values to the same type using the
+                  // configuration schema.
+                  sort($value);
+                  break;
+
+              }
+            }
+          }
+        }
+        return $value;
+      }
+
+      /**
+       * The constructor for passing the TypedConfigManager.
+       *
+       * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
+       *   The taped config manager.
+       */
+      public function __construct(TypedConfigManagerInterface $typedConfigManager) {
+        $this->typedConfigManager = $typedConfigManager;
+      }
+
+      /**
+       * {@inheritdoc}
+       */
+      public function save($has_trusted_data = FALSE) {
+        throw new \LogicException();
+      }
+
+      /**
+       * {@inheritdoc}
+       */
+      public function delete() {
+        throw new \LogicException();
+      }
+
+    };
+
+    // Sort the data using the core class we extended.
+    return $sorter->anonymousSort($name, $data);
+  }
+
+}
diff --git a/src/ConfigNormalizerInterface.php b/src/ConfigNormalizerInterface.php
new file mode 100644
index 0000000..7460da2
--- /dev/null
+++ b/src/ConfigNormalizerInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\config_normalizer;
+
+/**
+ * Defines an interface for config item normalizers.
+ *
+ * @api This is the main API of this module.
+ */
+interface ConfigNormalizerInterface {
+
+  /**
+   * Normalizes config for comparison.
+   *
+   * Normalization can help ensure that config from different storages can be
+   * compared meaningfully.
+   *
+   * @param string $name
+   *   The name of a configuration object to normalize.
+   * @param array $data
+   *   Configuration array to normalize.
+   *
+   * @return array
+   *   Normalized configuration array.
+   */
+  public function normalize($name, array $data);
+
+}
-- 
GitLab


From 491303628592b45ab7a4a9c59e52f114a4ea76ff Mon Sep 17 00:00:00 2001
From: Chris Green <42483-trackleft2@users.noreply.drupalcode.org>
Date: Tue, 18 Mar 2025 00:05:40 +0000
Subject: [PATCH 3/8] Edit ConfigNormalizerManager.php

---
 src/Plugin/ConfigNormalizerManager.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/Plugin/ConfigNormalizerManager.php b/src/Plugin/ConfigNormalizerManager.php
index b81a2a1..725784e 100644
--- a/src/Plugin/ConfigNormalizerManager.php
+++ b/src/Plugin/ConfigNormalizerManager.php
@@ -9,9 +9,6 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 
 /**
  * Provides the Config normalizer plugin manager.
- *
- * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
- * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerManager extends DefaultPluginManager {
 
-- 
GitLab


From 88a918d6fd49d51dba7cf03c50ae1a547873718c Mon Sep 17 00:00:00 2001
From: Chris Green <42483-trackleft2@users.noreply.drupalcode.org>
Date: Tue, 18 Mar 2025 00:06:06 +0000
Subject: [PATCH 4/8] Edit ConfigNormalizerInterface.php

---
 src/Plugin/ConfigNormalizerInterface.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/Plugin/ConfigNormalizerInterface.php b/src/Plugin/ConfigNormalizerInterface.php
index 7ecc549..608673e 100644
--- a/src/Plugin/ConfigNormalizerInterface.php
+++ b/src/Plugin/ConfigNormalizerInterface.php
@@ -6,9 +6,6 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
 
 /**
  * Defines an interface for Config normalizer plugins.
- *
- * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
- * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 interface ConfigNormalizerInterface extends PluginInspectionInterface {
 
-- 
GitLab


From e76939b8e1ce1cd49c789142d71a44aef3e0ed50 Mon Sep 17 00:00:00 2001
From: Chris Green <42483-trackleft2@users.noreply.drupalcode.org>
Date: Tue, 18 Mar 2025 00:06:18 +0000
Subject: [PATCH 5/8] Edit ConfigNormalizerBase.php

---
 src/Plugin/ConfigNormalizerBase.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/Plugin/ConfigNormalizerBase.php b/src/Plugin/ConfigNormalizerBase.php
index 6adecb3..1d7eb28 100644
--- a/src/Plugin/ConfigNormalizerBase.php
+++ b/src/Plugin/ConfigNormalizerBase.php
@@ -9,9 +9,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Base class for Config normalizer plugins.
- *
- * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
- * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 abstract class ConfigNormalizerBase extends PluginBase implements ConfigNormalizerInterface {
 
-- 
GitLab


From 0750a5720bde4a33afffaf893b1b2a441da523b8 Mon Sep 17 00:00:00 2001
From: Chris Green <42483-trackleft2@users.noreply.drupalcode.org>
Date: Tue, 18 Mar 2025 00:06:34 +0000
Subject: [PATCH 6/8] Edit ConfigNormalizerActive.php

---
 src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php
index e79beea..264c70e 100644
--- a/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php
+++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerActive.php
@@ -14,9 +14,6 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  *   weight = 0,
  *   description = @Translation("Copies over properties that are set by core when configuration is saved to the active storage."),
  * )
- *
- * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
- * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerActive extends ConfigNormalizerBase implements ContainerFactoryPluginInterface {
 
-- 
GitLab


From fa8fcb88649415b42d476d7f93ecd61e623a6f74 Mon Sep 17 00:00:00 2001
From: Chris Green <42483-trackleft2@users.noreply.drupalcode.org>
Date: Tue, 18 Mar 2025 00:07:02 +0000
Subject: [PATCH 7/8] Edit ConfigNormalizerFilterFormat.php

---
 src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php
index 9558f50..9fd5665 100644
--- a/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php
+++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerFilterFormat.php
@@ -14,9 +14,6 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  *   weight = 20,
  *   description = @Translation("Removes the roles element from filter formats, since this element is valid only on exported configuration."),
  * )
- *
- * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
- * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerFilterFormat extends ConfigNormalizerBase implements ContainerFactoryPluginInterface {
 
-- 
GitLab


From d299537a79b0e383935e8e850f6ee8ed738b619e Mon Sep 17 00:00:00 2001
From: Chris Green <42483-trackleft2@users.noreply.drupalcode.org>
Date: Tue, 18 Mar 2025 00:07:10 +0000
Subject: [PATCH 8/8] Edit ConfigNormalizerSort.php

---
 src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php
index c1cf970..e627454 100644
--- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php
+++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php
@@ -15,9 +15,6 @@ use Symfony\Component\Yaml\Inline;
  *   weight = 20,
  *   description = @Translation("Recursively sorts a configuration array."),
  * )
- *
- * @deprecated in config_normalizer:2.0.0-alpha1 and is removed from config_normalizer:2.0.0. No replacement.
- * @see https://www.drupal.org/project/config_normalizer/issues/3230398
  */
 class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFactoryPluginInterface {
 
-- 
GitLab