Issue #1869566 by tim.plunkett, dawehner: Allow any collection of plugins to be lazily instantiated.
parent
8b1a049654
commit
81861b40e7
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Component\Plugin\PluginBag.
|
||||
*/
|
||||
|
||||
namespace Drupal\Component\Plugin;
|
||||
|
||||
/**
|
||||
* Defines an object which stores multiple plugin instances to lazy load them.
|
||||
*
|
||||
* The \ArrayAccess implementation is only for backwards compatibility, it is
|
||||
* deprecated and should not be used by new code.
|
||||
*/
|
||||
abstract class PluginBag implements \ArrayAccess, \Iterator, \Countable {
|
||||
|
||||
/**
|
||||
* Stores all instantiated plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pluginInstances = array();
|
||||
|
||||
/**
|
||||
* Stores the IDs of all potential plugin instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $instanceIDs = array();
|
||||
|
||||
/**
|
||||
* Initializes a plugin and stores the result in $this->pluginInstances.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance to initialize.
|
||||
*/
|
||||
abstract protected function initializePlugin($instance_id);
|
||||
|
||||
/**
|
||||
* Clears all instantiated plugins.
|
||||
*/
|
||||
public function clear() {
|
||||
$this->pluginInstances = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a plugin instance exists.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance to check.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the plugin instance exists, FALSE otherwise.
|
||||
*/
|
||||
public function has($instance_id) {
|
||||
return isset($this->pluginInstances[$instance_id]) || isset($this->instanceIDs[$instance_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a plugin instance, initializing it if necessary.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance being retrieved.
|
||||
*/
|
||||
public function get($instance_id) {
|
||||
if (!isset($this->pluginInstances[$instance_id])) {
|
||||
$this->initializePlugin($instance_id);
|
||||
}
|
||||
return $this->pluginInstances[$instance_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores an initialized plugin.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance being stored.
|
||||
* @param mixed $value
|
||||
* An instantiated plugin.
|
||||
*/
|
||||
public function set($instance_id, $value) {
|
||||
$this->pluginInstances[$instance_id] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an initialized plugin.
|
||||
*
|
||||
* The plugin can still be used, it will be reinitialized.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance to remove.
|
||||
*/
|
||||
public function remove($instance_id) {
|
||||
unset($this->pluginInstances[$instance_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetExists().
|
||||
*
|
||||
* This is deprecated, use \Drupal\Component\Plugin\PluginBag::has().
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->pluginInstances[$offset]) || isset($this->instanceIDs[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetGet().
|
||||
*
|
||||
* This is deprecated, use \Drupal\Component\Plugin\PluginBag::get().
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
if (!isset($this->pluginInstances[$offset])) {
|
||||
$this->initializePlugin($offset);
|
||||
}
|
||||
return $this->pluginInstances[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetSet().
|
||||
*
|
||||
* This is deprecated, use \Drupal\Component\Plugin\PluginBag::set().
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->pluginInstances[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetUnset().
|
||||
*
|
||||
* This is deprecated, use \Drupal\Component\Plugin\PluginBag::remove().
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->pluginInstances[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::current().
|
||||
*/
|
||||
public function current() {
|
||||
return $this->offsetGet($this->key());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::next().
|
||||
*/
|
||||
public function next() {
|
||||
next($this->instanceIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::key().
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->instanceIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::valid().
|
||||
*/
|
||||
public function valid() {
|
||||
$key = key($this->instanceIDs);
|
||||
return $key !== NULL && $key !== FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::rewind().
|
||||
*/
|
||||
public function rewind() {
|
||||
reset($this->instanceIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Countable::count().
|
||||
*/
|
||||
public function count() {
|
||||
return count($this->instanceIDs);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Plugin\PluginBagTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Plugin;
|
||||
|
||||
use Drupal\plugin_test\Plugin\TestPluginBag;
|
||||
use Drupal\plugin_test\Plugin\plugin_test\mock_block\MockTestPluginInterface;
|
||||
|
||||
/**
|
||||
* Tests the generic plugin bag.
|
||||
*
|
||||
* @see \Drupal\Component\Plugin\PluginBag
|
||||
* @see \Drupal\plugin_test\Plugin\TestPluginBag
|
||||
*/
|
||||
class PluginBagTest extends PluginTestBase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Plugin Bag',
|
||||
'description' => 'Tests the generic plugin bag.',
|
||||
'group' => 'Plugin API',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the generic plugin bag.
|
||||
*/
|
||||
protected function testPluginBag() {
|
||||
// Setup the plugin bag as well as the available plugin definitions.
|
||||
$plugin_bag = new TestPluginBag($this->mockBlockManager);
|
||||
$definitions = $this->mockBlockManager->getDefinitions();
|
||||
$first_instance_id = key($definitions);
|
||||
|
||||
foreach ($definitions as $instance_id => $definition) {
|
||||
$this->assertTrue(isset($plugin_bag[$instance_id]), format_string('Plugin instance @instance_id exits on the bag', array('@instance_id' => $instance_id)));
|
||||
$this->assertTrue($plugin_bag->has($instance_id), format_string('Plugin instance @instance_id exits on the bag', array('@instance_id' => $instance_id)));
|
||||
$this->assertTrue($plugin_bag[$instance_id] instanceof $definition['class'], 'Getting the plugin from the bag worked.');
|
||||
$this->assertTrue($plugin_bag->get($instance_id) instanceof $definition['class'], 'Getting the plugin from the bag worked.');
|
||||
}
|
||||
|
||||
// A non existing instance_id shouldn't exist on the bag.
|
||||
$random_name = $this->randomName();
|
||||
$random_name_2 = $this->randomName();
|
||||
$this->assertFalse(isset($plugin_bag[$random_name]), 'A random instance_id should not exist on the plugin bag.');
|
||||
$this->assertFalse($plugin_bag->has($random_name_2), 'A random instance_id should not exist on the plugin bag.');
|
||||
|
||||
// Set a new plugin instance to the bag, to test offsetSet.
|
||||
$plugin_bag[$random_name] = $this->mockBlockManager->createInstance($first_instance_id, array());
|
||||
$plugin_bag->set($random_name_2, $this->mockBlockManager->createInstance($first_instance_id, array()));
|
||||
$this->assertTrue(isset($plugin_bag[$random_name]), 'A random instance_id should exist after manual setting on the plugin bag.');
|
||||
$this->assertTrue(isset($plugin_bag[$random_name_2]), 'A random instance_id should exist after manual setting on the plugin bag.');
|
||||
$this->assertTrue($plugin_bag->has($random_name), 'A random instance_id should exist after manual setting on the plugin bag.');
|
||||
$this->assertTrue($plugin_bag->has($random_name_2), 'A random instance_id should exist after manual setting on the plugin bag.');
|
||||
|
||||
// Remove the previous added element and check whether it still exists.
|
||||
unset($plugin_bag[$random_name]);
|
||||
$plugin_bag->remove($random_name_2);
|
||||
$this->assertFalse(isset($plugin_bag[$random_name]), 'A random instance_id should not exist on the plugin bag after removing.');
|
||||
$this->assertFalse(isset($plugin_bag[$random_name_2]), 'A random instance_id should not exist on the plugin bag after removing.');
|
||||
$this->assertFalse($plugin_bag->has($random_name), 'A random instance_id should not exist on the plugin bag after removing.');
|
||||
$this->assertFalse($plugin_bag->has($random_name_2), 'A random instance_id should not exist on the plugin bag after removing.');
|
||||
|
||||
// Test that iterating over the plugins work.
|
||||
$expected_instance_ids = array_keys($definitions);
|
||||
$counter = 0;
|
||||
foreach ($plugin_bag as $instance_id => $plugin) {
|
||||
$this->assertEqual($expected_instance_ids[$counter], $instance_id, format_string('The iteration works as expected for plugin instance @instance_id', array('@instance_id' => $instance_id)));
|
||||
$counter++;
|
||||
}
|
||||
|
||||
$this->assertEqual(count($plugin_bag), count($expected_instance_ids), 'The amount of items in plugin bag is as expected.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\plugin_test\Plugin\TestPluginBag.
|
||||
*/
|
||||
|
||||
namespace Drupal\plugin_test\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBag;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines a plugin bag which uses fruit plugins.
|
||||
*/
|
||||
class TestPluginBag extends PluginBag {
|
||||
|
||||
/**
|
||||
* Stores the plugin manager used by this bag.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a TestPluginBag object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
|
||||
* The plugin manager that handles test plugins.
|
||||
*/
|
||||
public function __construct(PluginManagerInterface $manager) {
|
||||
$this->manager = $manager;
|
||||
|
||||
$this->instanceIDs = drupal_map_assoc(array_keys($this->manager->getDefinitions()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Component\Plugin\PluginBag::initializePlugin().
|
||||
*/
|
||||
protected function initializePlugin($instance_id) {
|
||||
// If the plugin was initialized before, just return.
|
||||
if (isset($this->pluginInstances[$instance_id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pluginInstances[$instance_id] = $this->manager->createInstance($instance_id, array());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views\DisplayArray.
|
||||
*/
|
||||
|
||||
namespace Drupal\views;
|
||||
|
||||
/**
|
||||
* A class which wraps the displays of a view so you can lazy-initialize them.
|
||||
*/
|
||||
class DisplayArray implements \ArrayAccess, \Iterator, \Countable {
|
||||
|
||||
/**
|
||||
* Stores a reference to the view which has this displays attached.
|
||||
*
|
||||
* @var \Drupal\views\ViewExecutable
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Stores the actual display instances in an array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $displayHandlers = array();
|
||||
|
||||
/**
|
||||
* Stores all display IDs, coming from $this->view->storage->get('display').
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $displayIDs;
|
||||
|
||||
/**
|
||||
* Constructs a DisplayArray object.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable
|
||||
* The view which has this displays attached.
|
||||
*/
|
||||
public function __construct(ViewExecutable $view) {
|
||||
$this->view = $view;
|
||||
|
||||
$this->initializeDisplay('default');
|
||||
|
||||
// Store all display IDs to access them easy and fast.
|
||||
$display = $this->view->storage->get('display');
|
||||
$this->displayIDs = drupal_map_assoc(array_keys($display));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructs a DisplayArray object.
|
||||
*/
|
||||
public function __destruct() {
|
||||
foreach ($this->displayHandlers as $display_id => $display) {
|
||||
$display->destroy();
|
||||
unset($this->displayHandlers[$display_id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a single display and stores the result in $this->displayHandlers.
|
||||
*
|
||||
* @param string $display_id
|
||||
* The name of the display to initialize.
|
||||
*/
|
||||
protected function initializeDisplay($display_id) {
|
||||
// If the display was initialized before, just return.
|
||||
if (isset($this->displayHandlers[$display_id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve and initialize the new display handler with data.
|
||||
$display = &$this->view->storage->getDisplay($display_id);
|
||||
$this->displayHandlers[$display_id] = drupal_container()->get("plugin.manager.views.display")->createInstance($display['display_plugin']);
|
||||
if (empty($this->displayHandlers[$display_id])) {
|
||||
// Provide a 'default' handler as an emergency. This won't work well but
|
||||
// it will keep things from crashing.
|
||||
$this->displayHandlers[$display_id] = drupal_container()->get("plugin.manager.views.display")->createInstance('default');
|
||||
}
|
||||
|
||||
$this->displayHandlers[$display_id]->initDisplay($this->view, $display);
|
||||
// If this is not the default display handler, let it know which is since
|
||||
// it may well utilize some data from the default.
|
||||
if ($display_id != 'default') {
|
||||
$this->displayHandlers[$display_id]->default_display = $this->displayHandlers['default'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetExists().
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->displayHandlers[$offset]) || isset($this->displayIDs[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetGet().
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
if (!isset($this->displayHandlers[$offset])) {
|
||||
$this->initializeDisplay($offset);
|
||||
}
|
||||
return $this->displayHandlers[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetSet().
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->displayHandlers[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetUnset().
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->displayHandlers[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::current().
|
||||
*/
|
||||
public function current() {
|
||||
return $this->offsetGet($this->key());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::next().
|
||||
*/
|
||||
public function next() {
|
||||
next($this->displayIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::key().
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->displayIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::valid().
|
||||
*/
|
||||
public function valid() {
|
||||
$key = key($this->displayIDs);
|
||||
return $key !== NULL && $key !== FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Iterator::rewind().
|
||||
*/
|
||||
public function rewind() {
|
||||
reset($this->displayIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Countable::count().
|
||||
*/
|
||||
public function count() {
|
||||
return count($this->displayIDs);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views\DisplayBag.
|
||||
*/
|
||||
|
||||
namespace Drupal\views;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBag;
|
||||
|
||||
/**
|
||||
* A class which wraps the displays of a view so you can lazy-initialize them.
|
||||
*/
|
||||
class DisplayBag extends PluginBag {
|
||||
|
||||
/**
|
||||
* Stores a reference to the view which has this displays attached.
|
||||
*
|
||||
* @var \Drupal\views\ViewExecutable
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Constructs a DisplayBag object.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable
|
||||
* The view which has this displays attached.
|
||||
*/
|
||||
public function __construct(ViewExecutable $view) {
|
||||
$this->view = $view;
|
||||
|
||||
$this->initializePlugin('default');
|
||||
|
||||
// Store all display IDs to access them easy and fast.
|
||||
$display = $this->view->storage->get('display');
|
||||
$this->instanceIDs = drupal_map_assoc(array_keys($display));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructs a DisplayBag object.
|
||||
*/
|
||||
public function __destruct() {
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Component\Plugin\PluginBag::clear().
|
||||
*/
|
||||
public function clear() {
|
||||
foreach ($this->pluginInstances as $display_id => $display) {
|
||||
$display->destroy();
|
||||
}
|
||||
|
||||
parent::clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Component\Plugin\PluginBag::initializePlugin().
|
||||
*/
|
||||
protected function initializePlugin($display_id) {
|
||||
// If the display was initialized before, just return.
|
||||
if (isset($this->pluginInstances[$display_id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve and initialize the new display handler with data.
|
||||
$display = &$this->view->storage->getDisplay($display_id);
|
||||
$this->pluginInstances[$display_id] = drupal_container()->get("plugin.manager.views.display")->createInstance($display['display_plugin']);
|
||||
if (empty($this->pluginInstances[$display_id])) {
|
||||
// Provide a 'default' handler as an emergency. This won't work well but
|
||||
// it will keep things from crashing.
|
||||
$this->pluginInstances[$display_id] = drupal_container()->get("plugin.manager.views.display")->createInstance('default');
|
||||
}
|
||||
|
||||
$this->pluginInstances[$display_id]->initDisplay($this->view, $display);
|
||||
// If this is not the default display handler, let it know which is since
|
||||
// it may well utilize some data from the default.
|
||||
if ($display_id != 'default') {
|
||||
$this->pluginInstances[$display_id]->default_display = $this->pluginInstances['default'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,7 @@ namespace Drupal\views\Tests;
|
|||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\DisplayArray;
|
||||
use Drupal\views\DisplayBag;
|
||||
use Drupal\views\Plugin\views\display\DefaultDisplay;
|
||||
use Drupal\views\Plugin\views\display\Page;
|
||||
use Drupal\views\Plugin\views\style\DefaultStyle;
|
||||
|
@ -152,7 +152,7 @@ class ViewExecutableTest extends ViewUnitTestBase {
|
|||
|
||||
// Tests Drupal\views\ViewExecutable::initDisplay().
|
||||
$view->initDisplay();
|
||||
$this->assertTrue($view->displayHandlers instanceof DisplayArray, 'The displayHandlers property has the right class.');
|
||||
$this->assertTrue($view->displayHandlers instanceof DisplayBag, 'The displayHandlers property has the right class.');
|
||||
// Tests the classes of the instances.
|
||||
$this->assertTrue($view->displayHandlers['default'] instanceof DefaultDisplay);
|
||||
$this->assertTrue($view->displayHandlers['page_1'] instanceof Page);
|
||||
|
|
|
@ -597,7 +597,7 @@ class ViewExecutable {
|
|||
}
|
||||
|
||||
// Initialize the display cache array.
|
||||
$this->displayHandlers = new DisplayArray($this);
|
||||
$this->displayHandlers = new DisplayBag($this);
|
||||
|
||||
$this->current_display = 'default';
|
||||
$this->display_handler = $this->displayHandlers['default'];
|
||||
|
|
Loading…
Reference in New Issue