diff --git a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php index d049814..ebada0a 100644 --- a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php +++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php @@ -31,6 +31,13 @@ class DefaultFactory implements FactoryInterface { protected $discovery; /** + * Stores the interface the plugin should have. + * + * @var string + */ + protected $interface; + + /** * Constructs a Drupal\Component\Plugin\Factory\DefaultFactory object. */ public function __construct(DiscoveryInterface $discovery) { @@ -38,11 +45,31 @@ public function __construct(DiscoveryInterface $discovery) { } /** + * Set the interface used for the plugin. + * + * @param string + * The interface the plugin should have. + */ + public function setInterface($interface) { + $this->interface = $interface; + } + + /** * Implements Drupal\Component\Plugin\Factory\FactoryInterface::createInstance(). */ public function createInstance($plugin_id, array $configuration) { $plugin_definition = $this->discovery->getDefinition($plugin_id); $plugin_class = static::getPluginClass($plugin_id, $plugin_definition); + + // Validate the interface of the class. + if (isset($this->interface)) { + $interfaces = class_implements($plugin_class); + if (!isset($interfaces[$this->interface])) { + throw new PluginException(sprintf('Plugin (%s) instance class "%s" has to implement interface %s', $plugin_id, $plugin_class, $this->interface)); + } + } + + return new $plugin_class($configuration, $plugin_id, $plugin_definition); } diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Apple.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Apple.php index fd3fb2f..6a59327 100644 --- a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Apple.php +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Apple.php @@ -16,5 +16,7 @@ * color = "green" * ) */ -class Apple {} +class Apple implements FruitInterface { + +} diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Banana.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Banana.php index 55f1d12..71f5bb1 100644 --- a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Banana.php +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/Banana.php @@ -20,6 +20,6 @@ * } * ) */ -class Banana { +class Banana implements FruitInterface { } diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/FruitInterface.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/FruitInterface.php new file mode 100644 index 0000000..a55efc7 --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/FruitInterface.php @@ -0,0 +1,15 @@ + 'Default plugin factory', + 'description' => 'Tests the default plugin factory.', + 'group' => 'Plugin', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface'); + $this->defaultFactory = new DefaultFactory($this->discovery); + } + + /** + * Tests the getPluginClass method. + * + * @see \Drupal\Component\Plugin\DefaultFactory::getPluginClass + * @dataProvider providerTestGetPluginClass + */ + public function testGetPluginClass($plugin_id, $plugin_definition) { + $this->discovery->expects($this->once()) + ->method('getDefinition') + ->with($plugin_id) + ->will($this->returnValue($plugin_definition)); + $this->defaultFactory->setInterface('Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface'); + $instance = $this->defaultFactory->createInstance($plugin_id, array()); + + $this->assertInstanceOf($plugin_definition['class'], $instance); + $this->assertInstanceOf('Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface', $instance); + } + + /** + * Provides plugin_id/plugin_definition which are valid. + * + * @return array + * An array of plugin IDs and plugin definitions. + * + * @see self::testGetPluginClassWithInvalidInput + */ + public function providerTestGetPluginClass() { + return array( + array('apple', array('class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Apple')), + array('banana', array('class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Banana')), + ); + } + + + /** + * Tests the getPluginClass method with invalid input. + * + * @expectedException \Drupal\Component\Plugin\Exception\PluginException + * @dataProvider providerTestGetPluginClassWithInvalidInput + */ + public function testGetPluginClassWithInvalidInput($plugin_id, $plugin_definition) { + $this->discovery->expects($this->once()) + ->method('getDefinition') + ->with($plugin_id) + ->will($this->returnValue($plugin_definition)); + $this->defaultFactory->setInterface('Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface'); + $this->defaultFactory->createInstance($plugin_id, array()); + } + + /** + * Provides plugin_id/plugin_definitions which are invalid. + * + * @return array + * An array of plugin IDs and plugin definitions. + * + * @see self::testGetPluginClassWithInvalidInput + */ + public function providerTestGetPluginClassWithInvalidInput() { + return array( + // Provide no class definition. + array('rasperry', array()), + // Provide a non existing class. + array('rasperry', array('class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Rasperry')), + // Provide a plugin without FruitInterface. + array('orange', array('class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Orange')), + ); + } + +}