From 55463c9ef0e4c3e24a08a7c3c40c2ca12b087feb Mon Sep 17 00:00:00 2001
From: Dave Long <dave@longwaveconsulting.com>
Date: Fri, 10 May 2024 16:36:36 +0100
Subject: [PATCH] Issue #3445950 by alexpott, tim.plunkett:
 \Drupal\Core\Plugin\DefaultLazyPluginCollection::setInstanceConfiguration()
 assumes that $configuration results in the same plugin instance

(cherry picked from commit 926d069c2cf47fa251f5f5a51dd51d75027f53d6)
---
 .../Plugin/DefaultLazyPluginCollection.php    | 11 +++++++
 .../DefaultLazyPluginCollectionTest.php       | 31 +++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php b/core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php
index e420dfc15052..9371de650a92 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php
@@ -156,6 +156,17 @@ public function setConfiguration(array $configuration) {
    *   The plugin configuration to set.
    */
   public function setInstanceConfiguration($instance_id, array $configuration) {
+    if (
+      isset($this->pluginInstances[$instance_id]) &&
+      isset($configuration[$this->pluginKey]) &&
+      isset($this->configurations[$instance_id][$this->pluginKey]) &&
+      $configuration[$this->pluginKey] !== $this->configurations[$instance_id][$this->pluginKey]
+    ) {
+      // If the plugin has already been instantiated by the configuration was
+      // for a different plugin then we need to unset the instantiated plugin.
+      unset($this->pluginInstances[$instance_id]);
+    }
+
     $this->configurations[$instance_id] = $configuration;
     $instance = $this->get($instance_id);
     if ($instance instanceof ConfigurableInterface) {
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultLazyPluginCollectionTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultLazyPluginCollectionTest.php
index 71b67e43171d..a4b9e379caa9 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultLazyPluginCollectionTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultLazyPluginCollectionTest.php
@@ -168,6 +168,37 @@ public function testSetInstanceConfiguration() {
     $this->assertSame($expected, $config['cherry']);
   }
 
+  /**
+   * Tests plugin instances are changed if the configuration plugin key changes.
+   *
+   * @covers ::setInstanceConfiguration
+   */
+  public function testSetInstanceConfigurationPluginChange() {
+    $configurable_plugin = $this->prophesize(ConfigurableInterface::class);
+    $configurable_config = ['id' => 'configurable', 'foo' => 'bar'];
+    $configurable_plugin->getConfiguration()->willReturn($configurable_config);
+
+    $nonconfigurable_plugin = $this->prophesize(PluginInspectionInterface::class);
+    $nonconfigurable_config = ['id' => 'non-configurable', 'baz' => 'qux'];
+    $nonconfigurable_plugin->configuration = $nonconfigurable_config;
+
+    $configurations = [
+      'instance' => $configurable_config,
+    ];
+
+    $plugin_manager = $this->prophesize(PluginManagerInterface::class);
+    $plugin_manager->createInstance('configurable', $configurable_config)->willReturn($configurable_plugin->reveal());
+    $plugin_manager->createInstance('non-configurable', $nonconfigurable_config)->willReturn($nonconfigurable_plugin->reveal());
+
+    $collection = new DefaultLazyPluginCollection($plugin_manager->reveal(), $configurations);
+    $this->assertInstanceOf(ConfigurableInterface::class, $collection->get('instance'));
+
+    // Ensure changing the instance to a different plugin via
+    // setInstanceConfiguration() results in a different plugin instance.
+    $collection->setInstanceConfiguration('instance', $nonconfigurable_config);
+    $this->assertNotInstanceOf(ConfigurableInterface::class, $collection->get('instance'));
+  }
+
   /**
    * @covers ::count
    */
-- 
GitLab