From b7540532dcbf5988ad69525fad64f5a5f8aae60f Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 14 Nov 2023 22:46:13 +0000
Subject: [PATCH] Issue #3401186 by Wim Leers, alexpott: Follow-up for
 #3382510: Throw \LogicException when >1 #config_target in the same form
 targets the same property path

---
 core/lib/Drupal/Core/Form/ConfigFormBase.php  |  8 ++++
 .../Tests/Core/Form/ConfigTargetTest.php      | 44 +++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/core/lib/Drupal/Core/Form/ConfigFormBase.php b/core/lib/Drupal/Core/Form/ConfigFormBase.php
index 0383bfe443a9..daf6f409b20a 100644
--- a/core/lib/Drupal/Core/Form/ConfigFormBase.php
+++ b/core/lib/Drupal/Core/Form/ConfigFormBase.php
@@ -136,6 +136,14 @@ public function storeConfigKeyToFormElementMap(array $element, FormStateInterfac
         $target = ConfigTarget::fromString($target);
       }
       foreach ($target->propertyPaths as $property_path) {
+        if (isset($map[$target->configName][$property_path])) {
+          throw new \LogicException(sprintf('Two #config_targets both target "%s" in the "%s" config: `%s` and `%s`.',
+            $property_path,
+            $target->configName,
+            '$form[\'' . implode("']['", $map[$target->configName][$property_path]) . '\']',
+            '$form[\'' . implode("']['", $element['#array_parents']) . '\']',
+          ));
+        }
         $map[$target->configName][$property_path] = $element['#array_parents'];
       }
       $form_state->set(static::CONFIG_KEY_TO_FORM_ELEMENT_MAP, $map);
diff --git a/core/tests/Drupal/Tests/Core/Form/ConfigTargetTest.php b/core/tests/Drupal/Tests/Core/Form/ConfigTargetTest.php
index 01de877eff6d..3932d14552a0 100644
--- a/core/tests/Drupal/Tests/Core/Form/ConfigTargetTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/ConfigTargetTest.php
@@ -3,9 +3,14 @@
 namespace Drupal\Tests\Core\Form;
 
 use Drupal\Core\Config\Config;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\ConfigTarget;
 use Drupal\Core\Form\ToConfig;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\RedundantEditableConfigNamesTrait;
 use Drupal\Tests\UnitTestCase;
 use Prophecy\Argument;
 
@@ -15,6 +20,45 @@
  */
 class ConfigTargetTest extends UnitTestCase {
 
+  /**
+   * @covers \Drupal\Core\Form\ConfigFormBase::storeConfigKeyToFormElementMap
+   */
+  public function testDuplicateTargetsNotAllowed(): void {
+    $form = [
+      'test' => [
+        '#type' => 'text',
+        '#default_value' => 'A test',
+        '#config_target' => new ConfigTarget('system.site', 'admin_compact_mode', 'intval', 'boolval'),
+        '#name' => 'test',
+        '#array_parents' => ['test'],
+      ],
+      'duplicate' => [
+        '#type' => 'text',
+        '#config_target' => new ConfigTarget('system.site', 'admin_compact_mode', 'intval', 'boolval'),
+        '#name' => 'duplicate',
+        '#array_parents' => ['duplicate'],
+      ],
+    ];
+
+    $test_form = new class(
+      $this->prophesize(ConfigFactoryInterface::class)->reveal(),
+      $this->prophesize(TypedConfigManagerInterface::class)->reveal(),
+    ) extends ConfigFormBase {
+      use RedundantEditableConfigNamesTrait;
+
+      public function getFormId() {
+        return 'test';
+      }
+
+    };
+    $form_state = new FormState();
+    $test_form->storeConfigKeyToFormElementMap($form['test'], $form_state);
+
+    $this->expectException(\LogicException::class);
+    $this->expectExceptionMessage('Two #config_targets both target "admin_compact_mode" in the "system.site" config: `$form[\'test\']` and `$form[\'duplicate\']`.');
+    $test_form->storeConfigKeyToFormElementMap($form['duplicate'], $form_state);
+  }
+
   /**
    * @covers ::fromForm
    * @covers ::fromString
-- 
GitLab