diff --git a/core/lib/Drupal/Component/Plugin/Discovery/CachedDiscoveryInterface.php b/core/lib/Drupal/Component/Plugin/Discovery/CachedDiscoveryInterface.php new file mode 100644 index 0000000..4810dae --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Discovery/CachedDiscoveryInterface.php @@ -0,0 +1,20 @@ +discovery instanceof CachedDiscoveryInterface) { + $this->discovery->clearCachedDefinitions(); + } + } + + /** * Implements Drupal\Component\Plugin\PluginManagerInterface::createInstance(). */ public function createInstance($plugin_id, array $configuration = array()) { diff --git a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php index 7a57395..4f81739 100644 --- a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php +++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php @@ -7,12 +7,13 @@ namespace Drupal\Core\Plugin\Discovery; +use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; use Drupal\Component\Plugin\Discovery\DiscoveryInterface; /** * Enables static and persistent caching of discovered plugin definitions. */ -class CacheDecorator implements DiscoveryInterface { +class CacheDecorator implements CachedDiscoveryInterface { /** * The cache key used to store the definition list. @@ -110,6 +111,16 @@ protected function setCachedDefinitions($definitions) { } /** + * Implements Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::clearCachedDefinitions. + */ + public function clearCachedDefinitions() { + if (isset($this->cacheKey)) { + cache($this->cacheBin)->delete($this->cacheKey); + } + $this->definitions = NULL; + } + + /** * Passes through all unknown calls onto the decorated object. */ public function __call($method, $args) { diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php index 6dc4163..87cf1c1 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php @@ -27,41 +27,16 @@ class FormatterPluginManager extends PluginManagerBase { ); /** - * The cache bin used for plugin definitions. - * - * @var string - */ - protected $cache_bin = 'field'; - - /** - * The cache id used for plugin definitions. - * - * @var string - */ - protected $cache_id = 'field_formatter_types'; - - /** * Constructs a FormatterPluginManager object. */ public function __construct() { - $this->baseDiscovery = new AlterDecorator(new FormatterLegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'formatter')), 'field_formatter_info'); - $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin); + $this->discovery = new AlterDecorator(new FormatterLegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'formatter')), 'field_formatter_info'); + $this->discovery = new CacheDecorator($this->discovery, 'field_formatter_types', 'field'); $this->factory = new FormatterFactory($this); } /** - * Clears cached definitions. - * - * @todo Remove when http://drupal.org/node/1764232 is fixed. - */ - public function clearDefinitions() { - // Clear 'static' data by creating a new object. - $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin); - cache($this->cache_bin)->delete($this->cache_id); - } - - /** * Overrides PluginManagerBase::getInstance(). */ public function getInstance(array $options) { diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php index a605129..064a252 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php @@ -28,41 +28,16 @@ class WidgetPluginManager extends PluginManagerBase { ); /** - * The cache bin used for plugin definitions. - * - * @var string - */ - protected $cache_bin = 'field'; - - /** - * The cache id used for plugin definitions. - * - * @var string - */ - protected $cache_id = 'field_widget_types'; - - /** * Constructs a WidgetPluginManager object. */ public function __construct() { - $this->baseDiscovery = new AlterDecorator(new WidgetLegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'widget')), 'field_widget_info'); - $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin); + $this->discovery = new AlterDecorator(new WidgetLegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'widget')), 'field_widget_info'); + $this->discovery = new CacheDecorator($this->discovery, 'field_widget_types', 'field'); $this->factory = new WidgetFactory($this); } /** - * Clears cached definitions. - * - * @todo Remove when http://drupal.org/node/1764232 is fixed. - */ - public function clearDefinitions() { - // Clear 'static' data by creating a new object. - $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin); - cache($this->cache_bin)->delete($this->cache_id); - } - - /** * Overrides Drupal\Component\Plugin\PluginManagerBase::getInstance(). */ public function getInstance(array $options) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php new file mode 100644 index 0000000..89a90a3 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php @@ -0,0 +1,136 @@ + 'CacheDecorator', + 'description' => 'Tests the CacheDecorator.', + 'group' => 'Plugin API', + ); + } + + public function setUp() { + global $conf; + + parent::setUp(); + + // Use a non-db cache backend, so that we can use DiscoveryTestBase (which + // extends UnitTestBase). + $conf['cache_classes'][$this->cacheBin] = 'Drupal\Core\Cache\MemoryBackend'; + + // Create discovery objects to test. + $this->emptyDiscovery = new StaticDiscovery(); + $this->emptyDiscovery = new CacheDecorator($this->emptyDiscovery, $this->cacheKey . '_empty', $this->cacheBin); + + $this->discovery = new StaticDiscovery(); + $this->discovery = new CacheDecorator($this->discovery, $this->cacheKey, $this->cacheBin); + + // Poluplate sample definitions. + $this->expectedDefinitions = array( + 'apple' => array( + 'label' => 'Apple', + 'color' => 'green', + ), + 'cherry' => array( + 'label' => 'Cherry', + 'color' => 'red', + ), + 'orange' => array( + 'label' => 'Orange', + 'color' => 'orange', + ), + ); + foreach ($this->expectedDefinitions as $plugin_id => $definition) { + $this->discovery->setDefinition($plugin_id, $definition); + } + } + + /** + * Tests that discovered definitions are properly cached. + * + * This comes in addition to DiscoveryTestBase::testDiscoveryInterface(), + * that test the basic discovery behavior. + */ + public function testCachedDefinitions() { + $cache = cache($this->cacheBin); + + // Check that nothing is cached initially. + $cached = $cache->get($this->cacheKey); + $this->assertIdentical($cached, FALSE, 'Cache is empty.'); + + // Get the definitions once, and check that they are present in the cache. + $definitions = $this->discovery->getDefinitions(); + $this->assertIdentical($definitions, $this->expectedDefinitions, 'Definitions are correctly retrieved.'); + $cached = $cache->get($this->cacheKey); + $this->assertIdentical($cached->data, $this->expectedDefinitions, 'Definitions are cached.'); + + // Check that the definitions are also cached in memory. Since the + // CacheDecorator::definitions property is protected, this is tested "from + // the outside" by wiping the cache entry, getting the definitions, and + // checking that the cache entry was not regenerated (thus showing that + // defintions were not fetched from the decorated discovery). + $cache->delete($this->cacheKey); + $definitions = $this->discovery->getDefinitions(); + $cached = $cache->get($this->cacheKey); + $this->assertIdentical($cached, FALSE, 'Cache is empty.'); + $this->assertIdentical($definitions, $this->expectedDefinitions, 'Definitions are cached in memory.'); + } + + /** + * Tests CacheDecorator::clearCachedDefinitions(). + */ + public function testClearCachedDefinitions() { + $cache = cache($this->cacheBin); + + // Populate the caches by collecting definitions once. + $this->discovery->getDefinitions(); + + // Add a new definition. + $this->expectedDefinitions['banana'] = array( + 'label' => 'Banana', + 'color' => 'yellow', + ); + $this->discovery->setDefinition('banana', $this->expectedDefinitions['banana']); + + // Check that the new definition is not found. + $definition = $this->discovery->getDefinition('banana'); + $this->assertNull($definition, 'Newly added definition is not found.'); + + // Clear cached definitions, and check that the new definition is found. + $this->discovery->clearCachedDefinitions(); + $cached = $cache->get($this->cacheKey); + $this->assertIdentical($cached, FALSE, 'Cache is empty.'); + $definitions = $this->discovery->getDefinitions(); + $this->assertIdentical($definitions, $this->expectedDefinitions, 'Newly added definition is found.'); + } + +}