diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php index f60c8b2..208bf0e 100644 --- a/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php +++ b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php @@ -50,7 +50,7 @@ public function getDefinition($plugin_id) { // If a plugin defined itself as a derivative, merge in possible // defaults from the derivative. if ($derivative_id && isset($plugin_definition)) { - $plugin_definition += $derivative_plugin_definition ?: array(); + $plugin_definition = $this->mergeDerivativeDefinition($plugin_definition, $derivative_plugin_definition); } else { $plugin_definition = $derivative_plugin_definition; @@ -90,10 +90,7 @@ protected function getDerivatives(array $base_plugin_definitions) { // Use this definition as defaults if a plugin already defined // itself as this derivative. if ($derivative_id && isset($base_plugin_definitions[$plugin_id])) { - $filtered_base = array_filter($base_plugin_definitions[$plugin_id]); - $derivative_definition = $filtered_base + ($derivative_definition ?: array()); - // Add back any empty keys that the derivative didn't have. - $derivative_definition += $base_plugin_definitions[$plugin_id]; + $derivative_definition = $this->mergeDerivativeDefinition($base_plugin_definitions[$plugin_id], $derivative_definition); } $plugin_definitions[$plugin_id] = $derivative_definition; } @@ -203,6 +200,26 @@ protected function getDerivativeClass($base_definition) { } /** + * Merges a base and derivative definition, taking into account empty values. + * + * @param array $base_plugin_definition + * The base plugin definition. + * @param array $derivative_definition + * The derivative plugin definition. + * + * @return array + * The merged definition. + */ + protected function mergeDerivativeDefinition($base_plugin_definition, $derivative_definition) { + // Use this definition as defaults if a plugin already defined itself as + // this derivative, but filter out empty values first. + $filtered_base = array_filter($base_plugin_definition); + $derivative_definition = $filtered_base + ($derivative_definition ?: array()); + // Add back any empty keys that the derivative didn't have. + return $derivative_definition + $base_plugin_definition; + } + + /** * Passes through all unknown calls onto the decorated object. */ public function __call($method, $args) { diff --git a/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php b/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php index 002f071..c22c201 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php @@ -159,4 +159,44 @@ public function testExistingDerivative() { $this->assertArrayEquals($expected, $returned_definitions['non_container_aware_discovery:test_discovery_1']); } + /** + * Tests a single definition when a derivative already exists. + */ + public function testSingleExistingDerivative() { + $base_definition = array( + 'id' => 'non_container_aware_discovery', + 'derivative' => '\Drupal\Tests\Core\Plugin\Discovery\TestDerivativeDiscovery', + 'string' => 'string', + 'empty_string' => 'not_empty', + 'array' => array('one', 'two'), + 'empty_array' => array('three'), + 'null_value' => TRUE, + ); + // This will clash with a derivative id. + // @see \Drupal\Tests\Core\Plugin\Discovery\TestDerivativeDiscovery + $derivative_definition = array( + 'id' => 'non_container_aware_discovery:test_discovery_1', + 'string' => 'string', + 'empty_string' => '', + 'array' => array('one', 'two'), + 'empty_array' => array(), + 'null_value' => NULL, + ); + + $this->discoveryMain->expects($this->at(0)) + ->method('getDefinition') + ->with('non_container_aware_discovery:test_discovery_1') + ->will($this->returnValue($derivative_definition)); + $this->discoveryMain->expects($this->at(1)) + ->method('getDefinition') + ->with('non_container_aware_discovery') + ->will($this->returnValue($base_definition)); + + $discovery = new DerivativeDiscoveryDecorator($this->discoveryMain); + + $expected = $base_definition; + $expected['id'] = 'non_container_aware_discovery:test_discovery_1'; + $this->assertArrayEquals($expected, $discovery->getDefinition('non_container_aware_discovery:test_discovery_1')); + } + }