diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php index f8af542..91fdce4 100644 --- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php @@ -32,10 +32,6 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition * {@inheritdoc} */ public function defaultConfiguration() { - // Note: The implementations returned default configuration should not - // contain the backward compatibility key 'handler_settings'. That will be - // automatically added in ::setConfiguration(). - // @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase::setConfiguration() return [ 'target_type' => NULL, // @todo Remove this key in Drupal 9.0.x. @@ -55,50 +51,17 @@ public function getConfiguration() { * {@inheritdoc} */ public function setConfiguration(array $configuration) { - - // In order to keep backward compatibility, we copy all settings, except - // 'target_type', 'handler' and 'entity' under 'handler_settings', following - // the structure from the field config. If the plugin was instantiated using - // the 'handler_settings' level, those values will be used. In case of - // conflict, the root level settings will take precedence. The backward - // compatibility aware configuration will have the next structure: - // - target_type - // - handler (will be removed in Drupal 9.0.x, it's the plugin id) - // - entity - // - setting_1 - // - setting_2 - // ... - // - setting_N - // - handler_settings: (will be removed in Drupal 9.0.x) - // - setting_1 - // - setting_2 - // ... - // - setting_N - // @todo Remove 'handler' and 'handler_settings' in Drupal 9.0.x. - - $default_config = $this->defaultConfiguration(); - if (isset($default_config['handler_settings'])) { - throw new \InvalidArgumentException("{$this->getPluginDefinition()['class']}::defaultConfiguration() should not contain a 'handler_settings' key. All settings should be placed in the root level."); - } - - // Extract the BC level from passed configuration, if any. - $handler_settings = !empty($configuration['handler_settings']) ? $configuration['handler_settings'] : []; - unset($configuration['handler_settings']); - - // Settings passed in the root level take precedence over BC settings. - $configuration += $handler_settings; + // Resolve BC level configurations, if any. + $this->resolveBcConfiguration($configuration); // Merge in defaults. - $this->configuration = NestedArray::mergeDeep($default_config, $configuration); + $this->configuration = NestedArray::mergeDeep( + $this->defaultConfiguration(), + $configuration + ); - // Synchronize back 'handler_settings'. - // @todo Remove this sync in Drupal 9.0.x - foreach ($this->configuration as $key => $value) { - // Filter out keys that belong strictly to the root level. - if (!in_array($key, ['handler', 'target_type', 'entity', 'handler_settings'])) { - $this->configuration['handler_settings'][$key] = $value; - } - } + // Assure a BC level configuration. + $this->assureBcConfiguration(); } /** @@ -130,4 +93,64 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s */ public function entityQueryAlter(SelectInterface $query) { } + /** + * Moves the BC level configurations in the right place. + * + * In order to keep backward compatibility, we copy all settings, except + * 'target_type', 'handler' and 'entity' under 'handler_settings', following + * the structure from the field config. If the plugin was instantiated using + * the 'handler_settings' level, those values will be used. In case of + * conflict, the root level settings will take precedence. The backward + * compatibility aware configuration will have the next structure: + * - target_type + * - handler (will be removed in Drupal 9.0.x, it's the plugin id) + * - entity + * - setting_1 + * - setting_2 + * ... + * - setting_N + * - handler_settings: (will be removed in Drupal 9.0.x) + * - setting_1 + * - setting_2 + * ... + * - setting_N + * + * @param array $configuration + * The configuration array to be altered. + * + * @deprecated Scheduled for removal in Drupal 9.0.x. + */ + protected function resolveBcConfiguration(array &$configuration) { + if (isset($this->defaultConfiguration()['handler_settings'])) { + throw new \InvalidArgumentException("{$this->getPluginDefinition()['class']}::defaultConfiguration() should not contain a 'handler_settings' key. All settings should be placed in the root level."); + } + + // Extract the BC level from the passed configuration, if any. + if (array_key_exists('handler_settings', $configuration)) { + if (!is_array($configuration['handler_settings'])) { + throw new \InvalidArgumentException("The setting 'handler_settings' is reserved and cannot be used."); + } + @trigger_error("Settings under 'handler_settings' are deprecated since version 8.3.x and will be removed before 9.0.0. Move the settings in the root of the configuration array.", E_USER_DEPRECATED); + + // Settings passed in the root level take precedence over BC settings. + $configuration += $configuration['handler_settings']; + unset($configuration['handler_settings']); + } + } + + /** + * Assures a BC level configuration. + * + * @deprecated Scheduled for removal in Drupal 9.0.x. + */ + protected function assureBcConfiguration() { + // Synchronize back 'handler_settings'. + foreach ($this->configuration as $key => $value) { + // Filter out keys that belong strictly to the root level. + if (!in_array($key, ['handler', 'target_type', 'entity', 'handler_settings'])) { + $this->configuration['handler_settings'][$key] = $value; + } + } + } + } diff --git a/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php b/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php index 0ad56b0..86aa53e 100644 --- a/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php +++ b/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php @@ -15,14 +15,51 @@ class EntityReferenceSelectionUnitTest extends UnitTestCase { /** + * Triggered errors. + * + * @var array + */ + protected $errors = []; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + set_error_handler([$this, 'errorHandler']); + } + + /** * Tests invalid default configuration. * * @covers ::defaultConfiguration + * @covers ::resolveBcConfiguration * @expectedException \InvalidArgumentException + * @expectedExceptionMessage TestSelectionWithInvalidDefaultConfiguration::defaultConfiguration() should not contain a 'handler_settings' key. All settings should be placed in the root level. */ public function testInvalidDefaultConfiguration() { - (new TestSelectionWithInvalidDefaultConfiguration([], 'test_selector', ['class' => 'TestSelectionWithInvalidDefaultConfiguration'])) - ->getConfiguration(); + new TestSelectionWithInvalidDefaultConfiguration( + [], + 'test_selector', + ['class' => 'TestSelectionWithInvalidDefaultConfiguration'] + ); + } + + /** + * Tests the selection handler with malformed 'handler_settings' value. + * + * @covers ::setConfiguration + * @covers ::resolveBcConfiguration + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The setting 'handler_settings' is reserved and cannot be used. + */ + public function testMalformedHandlerSettingsValue() { + new TestSelection( + // The deprecated 'handler_setting' should be an array. + ['handler_settings' => FALSE], + 'test_selector', + ['class' => 'TestSelectionWithInvalidDefaultConfiguration'] + ); } /** @@ -122,6 +159,7 @@ public function testSetConfiguration($options) { * Tests the selection handler plugin BC structure. * * @covers ::setConfiguration + * @covers ::resolveBcConfiguration */ public function testSetConfigurationBcLevel() { $config = [ @@ -157,6 +195,50 @@ public function testSetConfigurationBcLevel() { $this->assertArrayEquals($expected, $selection->getConfiguration()); } + /** + * Tests deprecation error triggering. + * + * @covers ::setConfiguration + * @covers ::resolveBcConfiguration + */ + public function testDeprecationErrorTriggering() { + $config = ['handler_settings' => ['settings1' => TRUE]]; + new TestSelection($config, 'test_selector', []); + $this->assertError("Settings under 'handler_settings' are deprecated since version 8.3.x and will be removed before 9.0.0. Move the settings in the root of the configuration array.", E_USER_DEPRECATED); + } + + /** + * Asserts an error has been triggered. + * + * @param string $message + * The error message. + * @param int $code + * The error code. + */ + protected function assertError($message, $code) { + $assertion_message = "Triggered error '$message' (code $code)."; + foreach ($this->errors as $error) { + if ($error['message'] === $message && $error['code'] == $code) { + $this->assertTrue(TRUE, $assertion_message); + return; + } + } + $this->fail($assertion_message); + } + + /** + * Provides a testing error handler. + * + * @param int $code + * @param string $message + * @param string $file + * @param int $line + * @param array $context + */ + public function errorHandler($code, $message, $file, $line, $context) { + $this->errors[] = compact('code', 'message', 'file', 'line', 'context'); + } + } /**