diff --git a/core/lib/Drupal/Core/Layout/DerivablePluginDefinitionInterface.php b/core/lib/Drupal/Component/Plugin/Definition/DerivablePluginDefinitionInterface.php similarity index 58% rename from core/lib/Drupal/Core/Layout/DerivablePluginDefinitionInterface.php rename to core/lib/Drupal/Component/Plugin/Definition/DerivablePluginDefinitionInterface.php index 3d24b9dea4c139197f10f01b14bdd74f72f98e5a..6aaa3adb31a3402b8acec5e2e4f5f9d985d9552b 100644 --- a/core/lib/Drupal/Core/Layout/DerivablePluginDefinitionInterface.php +++ b/core/lib/Drupal/Component/Plugin/Definition/DerivablePluginDefinitionInterface.php @@ -1,22 +1,11 @@ getDeriver(); + $id = $base_definition->id(); + } + if ((is_array($base_definition) || ($base_definition = (array) $base_definition)) && (isset($base_definition['deriver']))) { + $class = $base_definition['deriver']; + $id = $base_definition['id']; + } + if ($class) { if (!class_exists($class)) { - throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" does not exist.', $base_definition['id'], $class)); + throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" does not exist.', $id, $class)); } if (!is_subclass_of($class, '\Drupal\Component\Plugin\Derivative\DeriverInterface')) { - throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" must implement \Drupal\Component\Plugin\Derivative\DeriverInterface.', $base_definition['id'], $class)); + throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" must implement \Drupal\Component\Plugin\Derivative\DeriverInterface.', $id, $class)); } } return $class; diff --git a/core/lib/Drupal/Core/Layout/LayoutDefinition.php b/core/lib/Drupal/Core/Layout/LayoutDefinition.php index 794fff032d78ef7873621852bbfa85347d5922ad..a0a2e499ee3f414e42c0b17bcd7a507e21611e25 100644 --- a/core/lib/Drupal/Core/Layout/LayoutDefinition.php +++ b/core/lib/Drupal/Core/Layout/LayoutDefinition.php @@ -4,6 +4,7 @@ use Drupal\Component\Plugin\Definition\DependentPluginDefinitionInterface; use Drupal\Component\Plugin\Definition\DependentPluginDefinitionTrait; +use Drupal\Component\Plugin\Definition\DerivablePluginDefinitionInterface; use Drupal\Component\Plugin\Definition\PluginDefinitionInterface; use Drupal\Component\Plugin\Definition\PluginDefinition; diff --git a/core/lib/Drupal/Core/Layout/LayoutPluginManager.php b/core/lib/Drupal/Core/Layout/LayoutPluginManager.php index e1d0a1e6f455fd29464f3aadb3a96296184ff940..e11f1055ba10196e3c7b51795c09627d19014621 100644 --- a/core/lib/Drupal/Core/Layout/LayoutPluginManager.php +++ b/core/lib/Drupal/Core/Layout/LayoutPluginManager.php @@ -3,6 +3,7 @@ namespace Drupal\Core\Layout; use Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator; +use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator; use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -65,7 +66,7 @@ protected function getDiscovery() { $discovery = new AnnotatedClassDiscovery($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces); $discovery = new YamlDiscoveryDecorator($discovery, 'layouts', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories()); $discovery = new AnnotationBridgeDecorator($discovery, $this->pluginDefinitionAnnotationName); - $discovery = new ObjectDefinitionContainerDerivativeDiscoveryDecorator($discovery); + $discovery = new DerivativeDiscoveryDecorator($discovery); $this->discovery = $discovery; } return $this->discovery; diff --git a/core/lib/Drupal/Core/Layout/ObjectDefinitionContainerDerivativeDiscoveryDecorator.php b/core/lib/Drupal/Core/Layout/ObjectDefinitionContainerDerivativeDiscoveryDecorator.php deleted file mode 100644 index 02ecfb00925cdadd5a23c85efb270d30ee99cf0a..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Layout/ObjectDefinitionContainerDerivativeDiscoveryDecorator.php +++ /dev/null @@ -1,37 +0,0 @@ -getDeriver()) { - if (!class_exists($class)) { - throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" does not exist.', $base_definition['id'], $class)); - } - if (!is_subclass_of($class, '\Drupal\Component\Plugin\Derivative\DeriverInterface')) { - throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" must implement \Drupal\Component\Plugin\Derivative\DeriverInterface.', $base_definition['id'], $class)); - } - } - return $class; - } - -} diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php index aebb4730afd17c21c1c37818ecd8bd59154f4828..db4a05282d3a24c7e1d05ee65d5d19967c17eebc 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php @@ -35,6 +35,7 @@ public function __construct() { // A simple plugin: the user login block. $this->discovery->setDefinition('user_login', array( + 'id' => 'user_login', 'label' => t('User login'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserLoginBlock', )); @@ -46,11 +47,13 @@ public function __construct() { // MockMenuBlockDeriver class ensures that only derivatives, and not the // base plugin, are available to the system. $this->discovery->setDefinition('menu', array( + 'id' => 'menu', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockMenuBlock', 'deriver' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockMenuBlockDeriver', )); // A plugin defining itself as a derivative. $this->discovery->setDefinition('menu:foo', array( + 'id' => 'menu', 'label' => t('Base label'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockMenuBlock', )); @@ -62,6 +65,7 @@ public function __construct() { // MockLayoutBlockDeriver class ensures that both the base plugin and the // derivatives are available to the system. $this->discovery->setDefinition('layout', array( + 'id' => 'layout', 'label' => t('Layout'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockLayoutBlock', 'deriver' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockLayoutBlockDeriver', @@ -70,6 +74,7 @@ public function __construct() { // A block plugin that requires context to function. This block requires a // user object in order to return the user name from the getTitle() method. $this->discovery->setDefinition('user_name', array( + 'id' => 'user_name', 'label' => t('User name'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( @@ -79,6 +84,7 @@ public function __construct() { // An optional context version of the previous block plugin. $this->discovery->setDefinition('user_name_optional', array( + 'id' => 'user_name_optional', 'label' => t('User name optional'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( @@ -88,12 +94,14 @@ public function __construct() { // A block plugin that requires a typed data string context to function. $this->discovery->setDefinition('string_context', array( + 'id' => 'string_context', 'label' => t('String typed data'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\TypedDataStringBlock', )); // A complex context plugin that requires both a user and node for context. $this->discovery->setDefinition('complex_context', array( + 'id' => 'complex_context', 'label' => t('Complex context'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock', 'context' => array( diff --git a/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php b/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php index 3d651b15cd1b90bb76a02d26703973c5f1f5325d..dd5528b6da48d4da356f8701d761f38043033403 100644 --- a/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php +++ b/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php @@ -58,31 +58,38 @@ protected function setUp() { ); $this->mockBlockExpectedDefinitions = array( 'user_login' => array( + 'id' => 'user_login', 'label' => 'User login', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserLoginBlock', ), 'menu:main_menu' => array( + 'id' => 'menu', 'label' => 'Main menu', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockMenuBlock', ), 'menu:navigation' => array( + 'id' => 'menu', 'label' => 'Navigation', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockMenuBlock', ), 'menu:foo' => array( + 'id' => 'menu', 'label' => 'Base label', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockMenuBlock', 'setting' => 'default', ), 'layout' => array( + 'id' => 'layout', 'label' => 'Layout', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockLayoutBlock', ), 'layout:foo' => array( + 'id' => 'layout', 'label' => 'Layout Foo', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockLayoutBlock', ), 'user_name' => array( + 'id' => 'user_name', 'label' => 'User name', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( @@ -90,6 +97,7 @@ protected function setUp() { ), ), 'user_name_optional' => array( + 'id' => 'user_name_optional', 'label' => 'User name optional', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( @@ -97,10 +105,12 @@ protected function setUp() { ), ), 'string_context' => array( + 'id' => 'string_context', 'label' => 'String typed data', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\TypedDataStringBlock', ), 'complex_context' => array( + 'id' => 'complex_context', 'label' => 'Complex context', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock', 'context' => array( diff --git a/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php b/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php index d81360f0b2a18f99defae975ba2d8365a52e5985..1e96458342c68468779b1cbe51da49d7128bec6c 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/Discovery/DerivativeDiscoveryDecoratorTest.php @@ -2,12 +2,16 @@ namespace Drupal\Tests\Core\Plugin\Discovery; +use Drupal\Component\Plugin\Definition\DerivablePluginDefinitionInterface; use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator; +use Drupal\Component\Plugin\Exception\InvalidDeriverException; use Drupal\Tests\UnitTestCase; /** * Unit tests for the derivative discovery decorator. * + * @coversDefaultClass \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator + * * @group Plugin */ class DerivativeDiscoveryDecoratorTest extends UnitTestCase { @@ -82,6 +86,50 @@ public function testGetDerivativeFetcherWithAnnotationObjects() { $this->assertEquals('\Drupal\Tests\Core\Plugin\Discovery\TestDerivativeDiscoveryWithObject', $definitions['non_container_aware_discovery:test_discovery_1']->deriver); } + /** + * Tests getDeriverClass with classed objects instead of arrays. + * + * @covers ::getDeriverClass + */ + public function testGetDeriverClassWithClassedDefinitions() { + $definitions = array(); + $definition = $this->prophesize(DerivablePluginDefinitionInterface::class); + $definition->id()->willReturn('non_container_aware_discovery'); + $definition->getDeriver()->willReturn(TestDerivativeDiscoveryWithObject::class); + $definitions['non_container_aware_discovery'] = $definition->reveal(); + + $this->discoveryMain->expects($this->any()) + ->method('getDefinitions') + ->will($this->returnValue($definitions)); + + $discovery = new DerivativeDiscoveryDecorator($this->discoveryMain); + $definitions = $discovery->getDefinitions(); + + // Ensure that both test derivatives got added. + $this->assertContainsOnlyInstancesOf(DerivablePluginDefinitionInterface::class, $definitions); + $this->assertEquals(['non_container_aware_discovery:test_discovery_0', 'non_container_aware_discovery:test_discovery_1'], array_keys($definitions)); + } + + /** + * @covers ::getDeriverClass + */ + public function testGetDeriverClassWithInvalidClassedDefinitions() { + $definition = $this->prophesize(DerivablePluginDefinitionInterface::class); + $definition->id()->willReturn('non_existent_discovery'); + $definition->getDeriver()->willReturn('\Drupal\system\Tests\Plugin\NonExistentDeriver'); + + $definitions['non_existent_discovery'] = $definition->reveal(); + + $this->discoveryMain->expects($this->any()) + ->method('getDefinitions') + ->willReturn($definitions); + + $discovery = new DerivativeDiscoveryDecorator($this->discoveryMain); + + $this->setExpectedException(InvalidDeriverException::class, 'Plugin (non_existent_discovery) deriver "\Drupal\system\Tests\Plugin\NonExistentDeriver" does not exist.'); + $discovery->getDefinitions(); + } + /** * Tests the getDerivativeFetcher method with a non-existent class. *