diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php index 99abd89..15a67c2 100644 --- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php +++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Plugin; use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; +use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator; use Drupal\Component\Plugin\PluginManagerBase; use Drupal\Component\Plugin\PluginManagerInterface; @@ -99,6 +100,13 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt protected $defaults = array(); /** + * Defines a fallback ID in case the plugin could not be created. + * + * @var string + */ + protected $fallbackPluginId; + + /** * Creates the discovery object. * * @param string|bool $subdir @@ -112,12 +120,13 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt * (optional) The name of the annotation that contains the plugin definition. * Defaults to 'Drupal\Component\Annotation\Plugin'. */ - public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInterface $module_handler, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') { + public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInterface $module_handler, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', $fallback_plugin_id = NULL) { $this->subdir = $subdir; $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $plugin_definition_annotation_name); $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery); $this->factory = new ContainerFactory($this); $this->moduleHandler = $module_handler; + $this->fallbackPluginId = $fallback_plugin_id; } /** @@ -277,4 +286,40 @@ protected function findDefinitions() { return $definitions; } + /** + * {@inheritdoc} + */ + public function createInstance($plugin_id, array $configuration = array()) { + + try { + $instance = $this->factory->createInstance($plugin_id, $configuration); + } + catch (PluginException $exception) { + if (isset($this->fallbackPluginId)) { + // Allow implementations show the exception message. + $configuration['_exception'] = $exception; + $instance = $this->factory->createInstance($this->getFallbackPluginId($plugin_id, $configuration), $configuration); + } + else { + throw $exception; + } + } + return $instance; + } + + /** + * Returns the plugin ID to be used. + * + * @param string $original_plugin_id + * The plugin ID of the non-instantiable plugin. + * @param array $configuration + * The original configuration of the plugin. + * + * @return string + * The plugin ID of the fallback instance. + */ + protected function getFallbackPluginId($original_plugin_id, $configuration) { + return $this->fallbackPluginId; + } + } diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php index bd1efc2..5c0d888 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php @@ -7,7 +7,10 @@ namespace Drupal\Tests\Core\Plugin; +use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Component\Plugin\Factory\DefaultFactory; use Drupal\Core\Language\Language; +use Drupal\plugin_test\Plugin\plugin_test\fruit\Apple; use Drupal\Tests\UnitTestCase; /** @@ -212,6 +215,45 @@ public function testCacheClearWithTags() { $plugin_manager->clearCachedDefinitions(); } + /** + * Tests the plugin manager without a fallback plugin. + * + * @expectedException \Drupal\Component\Plugin\Exception\PluginException + */ + public function testCreateInstanceWithoutFallback() { + $factory = $this->getMock('\Drupal\Component\Plugin\Factory\FactoryInterface'); + $factory->expects($this->once()) + ->method('createInstance') + ->with('non_existing', array()) + ->will($this->throwException(new PluginException())); + + $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL); + $plugin_manager->setFactory($factory); + $plugin_manager->createInstance('non_existing'); + } + + /** + * Tests the plugin manager with a fallback plugin. + */ + public function testCreateInstanceWithFallback() { + $factory = $this->getMock('\Drupal\Component\Plugin\Factory\FactoryInterface'); + $apple = new Apple(); + $exception = new PluginException(); + $factory->expects($this->at(0)) + ->method('createInstance') + ->with('non_existing', array()) + ->will($this->throwException($exception)); + $factory->expects($this->at(1)) + ->method('createInstance') + ->with('apple', array('_exception' => $exception)) + ->will($this->returnValue($apple)); + + $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, 'apple'); + $plugin_manager->setFactory($factory); + $plugin = $plugin_manager->createInstance('non_existing'); + $this->assertSame($apple, $plugin); + } + } if (!defined('DRUPAL_ROOT')) { diff --git a/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php b/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php index a7ea79d..e977b6e 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php +++ b/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php @@ -8,6 +8,7 @@ namespace Drupal\Tests\Core\Plugin; use Drupal\Component\Plugin\Discovery\StaticDiscovery; +use Drupal\Component\Plugin\Factory\FactoryInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\DefaultPluginManager; @@ -28,8 +29,10 @@ class TestPluginManager extends DefaultPluginManager { * (optional) The module handler to invoke the alter hook with. * @param string $alter_hook * (optional) Name of the alter hook. + * @param string $fallback_plugin_id + * (optional) The fallback plugin ID. */ - public function __construct(\Traversable $namespaces, array $definitions, ModuleHandlerInterface $module_handler = NULL, $alter_hook = NULL) { + public function __construct(\Traversable $namespaces, array $definitions, ModuleHandlerInterface $module_handler = NULL, $alter_hook = NULL, $fallback_plugin_id = NULL) { // Create the object that can be used to return definitions for all the // plugins available for this type. Most real plugin managers use a richer // discovery implementation, but StaticDiscovery lets us add some simple @@ -46,6 +49,18 @@ public function __construct(\Traversable $namespaces, array $definitions, Module if ($alter_hook) { $this->alterInfo($alter_hook); } + + $this->fallbackPluginId = $fallback_plugin_id; + } + + /** + * Sets the plugin factory. + * + * @param \Drupal\Component\Plugin\Factory\FactoryInterface $factory + * (optional) The plugin factory. + */ + public function setFactory(FactoryInterface $factory) { + $this->factory = $factory; } }