Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
46
core/lib/Drupal/Core/Plugin/CachedDiscoveryClearer.php
Normal file
46
core/lib/Drupal/Core/Plugin/CachedDiscoveryClearer.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\CachedDiscoveryClearer.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class which is capable of clearing the cache on plugin managers.
|
||||
*/
|
||||
class CachedDiscoveryClearer {
|
||||
|
||||
/**
|
||||
* The stored discoveries.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface[]
|
||||
*/
|
||||
protected $cachedDiscoveries = array();
|
||||
|
||||
/**
|
||||
* Adds a plugin manager to the active list.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface $cached_discovery
|
||||
* An object that implements the cached discovery interface, typically a
|
||||
* plugin manager.
|
||||
*/
|
||||
public function addCachedDiscovery(CachedDiscoveryInterface $cached_discovery) {
|
||||
$this->cachedDiscoveries[] = $cached_discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cache on all cached discoveries.
|
||||
*/
|
||||
public function clearCachedDefinitions() {
|
||||
foreach ($this->cachedDiscoveries as $cached_discovery) {
|
||||
$cached_discovery->clearCachedDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
120
core/lib/Drupal/Core/Plugin/CategorizingPluginManagerTrait.php
Normal file
120
core/lib/Drupal/Core/Plugin/CategorizingPluginManagerTrait.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\CategorizingPluginManagerTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Provides a trait for the CategorizingPluginManagerInterface.
|
||||
*
|
||||
* The trait provides methods for categorizing plugin definitions based on a
|
||||
* 'category' key. The plugin manager should make sure there is a default
|
||||
* category. For that the trait's processDefinitionCategory() method can be
|
||||
* invoked from the processDefinition() method.
|
||||
*
|
||||
* @see \Drupal\Component\Plugin\CategorizingPluginManagerInterface
|
||||
*/
|
||||
trait CategorizingPluginManagerTrait {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Processes a plugin definition to ensure there is a category.
|
||||
*
|
||||
* If the definition lacks a category, it defaults to the providing module.
|
||||
*
|
||||
* @param array $definition
|
||||
* The plugin definition.
|
||||
*/
|
||||
protected function processDefinitionCategory(&$definition) {
|
||||
// Ensure that every plugin has a category.
|
||||
if (empty($definition['category'])) {
|
||||
// Default to the human readable module name if the provider is a module;
|
||||
// otherwise the provider machine name is used.
|
||||
$definition['category'] = $this->getProviderName($definition['provider']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of a provider.
|
||||
*
|
||||
* @param string $provider
|
||||
* The machine name of a plugin provider.
|
||||
*
|
||||
* @return string
|
||||
* The human-readable module name if it exists, otherwise the
|
||||
* machine-readable name passed.
|
||||
*/
|
||||
protected function getProviderName($provider) {
|
||||
$list = $this->getModuleHandler()->getModuleList();
|
||||
// If the module exists, return its human-readable name.
|
||||
if (isset($list[$provider])) {
|
||||
return $this->getModuleHandler()->getName($provider);
|
||||
}
|
||||
// Otherwise, return the machine name.
|
||||
return $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module handler used.
|
||||
*
|
||||
* @return \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
* The module handler.
|
||||
*/
|
||||
public function getModuleHandler() {
|
||||
// If the class has an injected module handler, use it. Otherwise fall back
|
||||
// to fetch it from the service container.
|
||||
if (isset($this->moduleHandler)) {
|
||||
return $this->moduleHandler;
|
||||
}
|
||||
return \Drupal::moduleHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Component\Plugin\CategorizingPluginManagerInterface::getCategories().
|
||||
*/
|
||||
public function getCategories() {
|
||||
/** @var \Drupal\Core\Plugin\CategorizingPluginManagerTrait|\Drupal\Component\Plugin\PluginManagerInterface $this */
|
||||
// Fetch all categories from definitions and remove duplicates.
|
||||
$categories = array_unique(array_values(array_map(function ($definition) {
|
||||
return $definition['category'];
|
||||
}, $this->getDefinitions())));
|
||||
natcasesort($categories);
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Component\Plugin\CategorizingPluginManagerInterface::getSortedDefinitions().
|
||||
*/
|
||||
public function getSortedDefinitions(array $definitions = NULL, $label_key = 'label') {
|
||||
// Sort the plugins first by category, then by label.
|
||||
/** @var \Drupal\Core\Plugin\CategorizingPluginManagerTrait|\Drupal\Component\Plugin\PluginManagerInterface $this */
|
||||
$definitions = isset($definitions) ? $definitions : $this->getDefinitions();
|
||||
uasort($definitions, function ($a, $b) use ($label_key) {
|
||||
if ($a['category'] != $b['category']) {
|
||||
return strnatcasecmp($a['category'], $b['category']);
|
||||
}
|
||||
return strnatcasecmp($a[$label_key], $b[$label_key]);
|
||||
});
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Component\Plugin\CategorizingPluginManagerInterface::getGroupedDefinitions().
|
||||
*/
|
||||
public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') {
|
||||
/** @var \Drupal\Core\Plugin\CategorizingPluginManagerTrait|\Drupal\Component\Plugin\PluginManagerInterface $this */
|
||||
$definitions = $this->getSortedDefinitions(isset($definitions) ? $definitions : $this->getDefinitions(), $label_key);
|
||||
$grouped_definitions = array();
|
||||
foreach ($definitions as $id => $definition) {
|
||||
$grouped_definitions[(string) $definition['category']][$id] = $definition;
|
||||
}
|
||||
return $grouped_definitions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\ContainerFactoryPluginInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for pulling plugin dependencies from the container.
|
||||
*/
|
||||
interface ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Creates an instance of the plugin.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The container to pull out services used in the plugin.
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
*
|
||||
* @return static
|
||||
* Returns an instance of this plugin.
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
}
|
116
core/lib/Drupal/Core/Plugin/Context/Context.php
Normal file
116
core/lib/Drupal/Core/Plugin/Context/Context.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\Context.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Plugin\Context\Context as ComponentContext;
|
||||
use Drupal\Component\Plugin\Exception\ContextException;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataTrait;
|
||||
|
||||
/**
|
||||
* A Drupal specific context wrapper class.
|
||||
*/
|
||||
class Context extends ComponentContext implements ContextInterface {
|
||||
|
||||
use TypedDataTrait;
|
||||
|
||||
/**
|
||||
* The data associated with the context.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected $contextData;
|
||||
|
||||
/**
|
||||
* The definition to which a context must conform.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface
|
||||
*/
|
||||
protected $contextDefinition;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextValue() {
|
||||
if (!isset($this->contextData)) {
|
||||
$definition = $this->getContextDefinition();
|
||||
$default_value = $definition->getDefaultValue();
|
||||
|
||||
if (isset($default_value)) {
|
||||
// Keep the default value here so that subsequent calls don't have to
|
||||
// look it up again.
|
||||
$this->setContextValue($default_value);
|
||||
}
|
||||
elseif ($definition->isRequired()) {
|
||||
$type = $definition->getDataType();
|
||||
throw new ContextException(SafeMarkup::format("The @type context is required and not present.", array('@type' => $type)));
|
||||
}
|
||||
return $default_value;
|
||||
}
|
||||
return $this->getTypedDataManager()->getCanonicalRepresentation($this->contextData);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContextValue($value) {
|
||||
if ($value instanceof TypedDataInterface) {
|
||||
return $this->setContextData($value);
|
||||
}
|
||||
else {
|
||||
return $this->setContextData($this->getTypedDataManager()->create($this->contextDefinition->getDataDefinition(), $value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return $this->contextDefinition->getConstraints();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextData() {
|
||||
if (!isset($this->contextData)) {
|
||||
$definition = $this->getContextDefinition();
|
||||
$default_value = $definition->getDefaultValue();
|
||||
if (isset($default_value)) {
|
||||
// Store the default value so that subsequent calls don't have to look
|
||||
// it up again.
|
||||
$this->contextData = $this->getTypedDataManager()->create($definition->getDataDefinition(), $default_value);
|
||||
}
|
||||
}
|
||||
return $this->contextData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContextData(TypedDataInterface $data) {
|
||||
$this->contextData = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextDefinition() {
|
||||
return $this->contextDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate() {
|
||||
return $this->getContextData()->validate();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextAwarePluginManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for plugin managers that support context-aware plugins.
|
||||
*/
|
||||
interface ContextAwarePluginManagerInterface extends PluginManagerInterface {
|
||||
|
||||
/**
|
||||
* Determines plugins whose constraints are satisfied by a set of contexts.
|
||||
*
|
||||
* @todo Use context definition objects after
|
||||
* https://www.drupal.org/node/2281635.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts.
|
||||
*
|
||||
* @return array
|
||||
* An array of plugin definitions.
|
||||
*/
|
||||
public function getDefinitionsForContexts(array $contexts = array());
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextAwarePluginManagerTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
/**
|
||||
* Provides a trait for plugin managers that support context-aware plugins.
|
||||
*/
|
||||
trait ContextAwarePluginManagerTrait {
|
||||
|
||||
/**
|
||||
* Wraps the context handler.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
|
||||
*/
|
||||
protected function contextHandler() {
|
||||
return \Drupal::service('context.handler');
|
||||
}
|
||||
|
||||
/**
|
||||
* See \Drupal\Core\Plugin\Context\ContextAwarePluginManagerInterface::getDefinitionsForContexts().
|
||||
*/
|
||||
public function getDefinitionsForContexts(array $contexts = array()) {
|
||||
return $this->contextHandler()->filterPluginDefinitionsByContexts($contexts, $this->getDefinitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* See \Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
|
||||
*/
|
||||
abstract public function getDefinitions();
|
||||
|
||||
}
|
258
core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
Normal file
258
core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
Normal file
|
@ -0,0 +1,258 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextDefinition.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\TypedData\TypedDataTrait;
|
||||
|
||||
/**
|
||||
* Defines a class for context definitions.
|
||||
*/
|
||||
class ContextDefinition implements ContextDefinitionInterface {
|
||||
|
||||
use TypedDataTrait;
|
||||
|
||||
/**
|
||||
* The data type of the data.
|
||||
*
|
||||
* @return string
|
||||
* The data type.
|
||||
*/
|
||||
protected $dataType;
|
||||
|
||||
/**
|
||||
* The human-readable label.
|
||||
*
|
||||
* @return string
|
||||
* The label.
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The human-readable description.
|
||||
*
|
||||
* @return string|null
|
||||
* The description, or NULL if no description is available.
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Whether the data is multi-valued, i.e. a list of data items.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isMultiple = FALSE;
|
||||
|
||||
/**
|
||||
* Determines whether a data value is required.
|
||||
*
|
||||
* @var bool
|
||||
* Whether a data value is required.
|
||||
*/
|
||||
protected $isRequired = TRUE;
|
||||
|
||||
/**
|
||||
* The default value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $defaultValue;
|
||||
|
||||
/**
|
||||
* An array of constraints.
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
protected $constraints = [];
|
||||
|
||||
/**
|
||||
* Creates a new context definition.
|
||||
*
|
||||
* @param string $data_type
|
||||
* The data type for which to create the context definition. Defaults to
|
||||
* 'any'.
|
||||
*
|
||||
* @return static
|
||||
* The created context definition object.
|
||||
*/
|
||||
public static function create($data_type = 'any') {
|
||||
return new static(
|
||||
$data_type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new context definition object.
|
||||
*
|
||||
* @param string $data_type
|
||||
* The required data type.
|
||||
* @param mixed string|null $label
|
||||
* The label of this context definition for the UI.
|
||||
* @param bool $required
|
||||
* Whether the context definition is required.
|
||||
* @param bool $multiple
|
||||
* Whether the context definition is multivalue.
|
||||
* @param string|null $description
|
||||
* The description of this context definition for the UI.
|
||||
* @param mixed $default_value
|
||||
* The default value of this definition.
|
||||
*/
|
||||
public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL, $default_value = NULL) {
|
||||
$this->dataType = $data_type;
|
||||
$this->label = $label;
|
||||
$this->isRequired = $required;
|
||||
$this->isMultiple = $multiple;
|
||||
$this->description = $description;
|
||||
$this->defaultValue = $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDataType() {
|
||||
return $this->dataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDataType($data_type) {
|
||||
$this->dataType = $data_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLabel($label) {
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDescription($description) {
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isMultiple() {
|
||||
return $this->isMultiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple($multiple = TRUE) {
|
||||
$this->isMultiple = $multiple;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRequired() {
|
||||
return $this->isRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRequired($required = TRUE) {
|
||||
$this->isRequired = $required;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultValue() {
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultValue($default_value) {
|
||||
$this->defaultValue = $default_value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
// @todo Apply defaults.
|
||||
return $this->constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraint($constraint_name) {
|
||||
$constraints = $this->getConstraints();
|
||||
return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConstraints(array $constraints) {
|
||||
$this->constraints = $constraints;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addConstraint($constraint_name, $options = NULL) {
|
||||
$this->constraints[$constraint_name] = $options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDataDefinition() {
|
||||
if ($this->isMultiple()) {
|
||||
$definition = $this->getTypedDataManager()->createListDataDefinition($this->getDataType());
|
||||
}
|
||||
else {
|
||||
$definition = $this->getTypedDataManager()->createDataDefinition($this->getDataType());
|
||||
}
|
||||
|
||||
if (!$definition) {
|
||||
throw new \Exception(SafeMarkup::format('The data type "@type" is invalid', array('@type' => $this->getDataType())));
|
||||
}
|
||||
$definition->setLabel($this->getLabel())
|
||||
->setDescription($this->getDescription())
|
||||
->setRequired($this->isRequired());
|
||||
$constraints = $definition->getConstraints() + $this->getConstraints();
|
||||
$definition->setConstraints($constraints);
|
||||
return $definition;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextDefinitionInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Plugin\Context\ContextDefinitionInterface as ComponentContextDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Interface for context definitions.
|
||||
*/
|
||||
interface ContextDefinitionInterface extends ComponentContextDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Returns the data definition of the defined context.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
* The data definition object.
|
||||
*/
|
||||
public function getDataDefinition();
|
||||
|
||||
}
|
93
core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
Normal file
93
core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\ContextException;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
|
||||
/**
|
||||
* Provides methods to handle sets of contexts.
|
||||
*/
|
||||
class ContextHandler implements ContextHandlerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
|
||||
return array_filter($definitions, function ($plugin_definition) use ($contexts) {
|
||||
// If this plugin doesn't need any context, it is available to use.
|
||||
if (!isset($plugin_definition['context'])) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Check the set of contexts against the requirements.
|
||||
return $this->checkRequirements($contexts, $plugin_definition['context']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkRequirements(array $contexts, array $requirements) {
|
||||
foreach ($requirements as $requirement) {
|
||||
if ($requirement->isRequired() && !$this->getMatchingContexts($contexts, $requirement)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMatchingContexts(array $contexts, ContextDefinitionInterface $definition) {
|
||||
return array_filter($contexts, function (ContextInterface $context) use ($definition) {
|
||||
$context_definition = $context->getContextDefinition();
|
||||
|
||||
// If the data types do not match, this context is invalid unless the
|
||||
// expected data type is any, which means all data types are supported.
|
||||
if ($definition->getDataType() != 'any' && $definition->getDataType() != $context_definition->getDataType()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If any constraint does not match, this context is invalid.
|
||||
foreach ($definition->getConstraints() as $constraint_name => $constraint) {
|
||||
if ($context_definition->getConstraint($constraint_name) != $constraint) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// All contexts with matching data type and contexts are valid.
|
||||
return TRUE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = array()) {
|
||||
$mappings += $plugin->getContextMapping();
|
||||
// Loop through each of the expected contexts.
|
||||
foreach (array_keys($plugin->getContextDefinitions()) as $plugin_context_id) {
|
||||
// If this context was given a specific name, use that.
|
||||
$context_id = isset($mappings[$plugin_context_id]) ? $mappings[$plugin_context_id] : $plugin_context_id;
|
||||
if (!empty($contexts[$context_id])) {
|
||||
// This assignment has been used, remove it.
|
||||
unset($mappings[$plugin_context_id]);
|
||||
$plugin->setContextValue($plugin_context_id, $contexts[$context_id]->getContextValue());
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any mappings that were not satisfied, throw an exception.
|
||||
if (!empty($mappings)) {
|
||||
throw new ContextException(SafeMarkup::format('Assigned contexts were not satisfied: @mappings', ['@mappings' => implode(',', array_keys($mappings))]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextHandlerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for handling sets of contexts.
|
||||
*/
|
||||
interface ContextHandlerInterface {
|
||||
|
||||
/**
|
||||
* Determines plugins whose constraints are satisfied by a set of contexts.
|
||||
*
|
||||
* @todo Use context definition objects after
|
||||
* https://www.drupal.org/node/2281635.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts.
|
||||
* @param array $definitions .
|
||||
* An array of plugin definitions.
|
||||
*
|
||||
* @return array
|
||||
* An array of plugin definitions.
|
||||
*/
|
||||
public function filterPluginDefinitionsByContexts(array $contexts, array $definitions);
|
||||
|
||||
/**
|
||||
* Checks a set of requirements against a set of contexts.
|
||||
*
|
||||
* @todo Use context definition objects after
|
||||
* https://www.drupal.org/node/2281635.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of available contexts.
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface[] $requirements
|
||||
* An array of requirements.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if all of the requirements are satisfied by the context, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
public function checkRequirements(array $contexts, array $requirements);
|
||||
|
||||
/**
|
||||
* Determines which contexts satisfy the constraints of a given definition.
|
||||
*
|
||||
* @todo Use context definition objects after
|
||||
* https://www.drupal.org/node/2281635.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts.
|
||||
* @param \Drupal\Core\Plugin\Context\ContextDefinitionInterface $definition
|
||||
* The definition to satisfy.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
||||
* An array of matching contexts.
|
||||
*/
|
||||
public function getMatchingContexts(array $contexts, ContextDefinitionInterface $definition);
|
||||
|
||||
/**
|
||||
* Prepares a plugin for evaluation.
|
||||
*
|
||||
* @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin
|
||||
* A plugin about to be evaluated.
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts to set on the plugin. They will only be set if they
|
||||
* match the plugin's context definitions.
|
||||
* @param array $mappings
|
||||
* (optional) A mapping of the expected assignment names to their context
|
||||
* names. For example, if one of the $contexts is named 'current_user', but the
|
||||
* plugin expects a context named 'user', then this map would contain
|
||||
* 'user' => 'current_user'.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\ContextException
|
||||
* Thrown when a context assignment was not satisfied.
|
||||
*/
|
||||
public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = array());
|
||||
|
||||
}
|
35
core/lib/Drupal/Core/Plugin/Context/ContextInterface.php
Normal file
35
core/lib/Drupal/Core/Plugin/Context/ContextInterface.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Context\ContextInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
|
||||
/**
|
||||
* Interface for context.
|
||||
*/
|
||||
interface ContextInterface extends ComponentContextInterface {
|
||||
|
||||
/**
|
||||
* Gets the context value as typed data object.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
public function getContextData();
|
||||
|
||||
/**
|
||||
* Sets the context value as typed data object.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $data
|
||||
* The context value as a typed data object.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContextData(TypedDataInterface $data);
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
/**
|
||||
* Handles context assignments for context-aware plugins.
|
||||
*/
|
||||
trait ContextAwarePluginAssignmentTrait {
|
||||
|
||||
/**
|
||||
* Ensures the t() method is available.
|
||||
*
|
||||
* @see \Drupal\Core\StringTranslation\StringTranslationTrait
|
||||
*/
|
||||
abstract protected function t($string, array $args = array(), array $options = array());
|
||||
|
||||
/**
|
||||
* Wraps the context handler.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
|
||||
*/
|
||||
protected function contextHandler() {
|
||||
return \Drupal::service('context.handler');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a form element for assigning a context to a given slot.
|
||||
*
|
||||
* @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin
|
||||
* The context-aware plugin.
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts.
|
||||
*
|
||||
* @return array
|
||||
* A form element for assigning context.
|
||||
*/
|
||||
protected function addContextAssignmentElement(ContextAwarePluginInterface $plugin, array $contexts) {
|
||||
$element = [];
|
||||
foreach ($plugin->getContextDefinitions() as $context_slot => $definition) {
|
||||
$valid_contexts = $this->contextHandler()->getMatchingContexts($contexts, $definition);
|
||||
$options = [];
|
||||
foreach ($valid_contexts as $context_id => $context) {
|
||||
$element['#tree'] = TRUE;
|
||||
$options[$context_id] = $context->getContextDefinition()->getLabel();
|
||||
$element[$context_slot] = [
|
||||
'#type' => 'value',
|
||||
'#value' => $context_id,
|
||||
];
|
||||
}
|
||||
|
||||
if (count($options) > 1) {
|
||||
$assignments = $plugin->getContextMapping();
|
||||
$element[$context_slot] = [
|
||||
'#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
|
||||
'#type' => 'select',
|
||||
'#options' => $options,
|
||||
'#required' => $definition->isRequired(),
|
||||
'#default_value' => !empty($assignments[$context_slot]) ? $assignments[$context_slot] : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
94
core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
Normal file
94
core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\ContextAwarePluginBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Component\Plugin\ContextAwarePluginBase as ComponentContextAwarePluginBase;
|
||||
use Drupal\Component\Plugin\Exception\ContextException;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\TypedData\TypedDataTrait;
|
||||
use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextInterface;
|
||||
|
||||
/**
|
||||
* Base class for plugins that are context aware.
|
||||
*/
|
||||
abstract class ContextAwarePluginBase extends ComponentContextAwarePluginBase implements ContextAwarePluginInterface {
|
||||
use TypedDataTrait;
|
||||
use StringTranslationTrait;
|
||||
use DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This code is identical to the Component in order to pick up a different
|
||||
* Context class.
|
||||
*/
|
||||
public function getContext($name) {
|
||||
// Check for a valid context value.
|
||||
if (!isset($this->context[$name])) {
|
||||
$this->context[$name] = new Context($this->getContextDefinition($name));
|
||||
}
|
||||
return $this->context[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContext($name, ComponentContextInterface $context) {
|
||||
// Check that the context passed is an instance of our extended interface.
|
||||
if (!$context instanceof ContextInterface) {
|
||||
throw new ContextException("Passed $name context must be an instance of \\Drupal\\Core\\Plugin\\Context\\ContextInterface");
|
||||
}
|
||||
parent::setContext($name, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextMapping() {
|
||||
$configuration = $this instanceof ConfigurablePluginInterface ? $this->getConfiguration() : $this->configuration;
|
||||
return isset($configuration['context_mapping']) ? $configuration['context_mapping'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContextMapping(array $context_mapping) {
|
||||
if ($this instanceof ConfigurablePluginInterface) {
|
||||
$configuration = $this->getConfiguration();
|
||||
$configuration['context_mapping'] = $context_mapping;
|
||||
$this->setConfiguration($configuration);
|
||||
}
|
||||
else {
|
||||
$this->configuration['context_mapping'] = $context_mapping;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface[]
|
||||
*/
|
||||
public function getContextDefinitions() {
|
||||
return parent::getContextDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the context handler.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
|
||||
*/
|
||||
protected function contextHandler() {
|
||||
return \Drupal::service('context.handler');
|
||||
}
|
||||
|
||||
}
|
42
core/lib/Drupal/Core/Plugin/ContextAwarePluginInterface.php
Normal file
42
core/lib/Drupal/Core/Plugin/ContextAwarePluginInterface.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\ContextAwarePluginInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\ContextAwarePluginInterface as ComponentContextAwarePluginInterface;
|
||||
|
||||
/**
|
||||
* An override of ContextAwarePluginInterface for documentation purposes.
|
||||
*
|
||||
* @see \Drupal\Component\Plugin\ContextAwarePluginInterface
|
||||
*
|
||||
* @ingroup plugin_api
|
||||
*/
|
||||
interface ContextAwarePluginInterface extends ComponentContextAwarePluginInterface {
|
||||
|
||||
/**
|
||||
* Gets the context definitions of the plugin.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface[]
|
||||
* The array of context definitions, keyed by context name.
|
||||
*/
|
||||
public function getContextDefinitions();
|
||||
|
||||
/**
|
||||
* Gets a specific context definition of the plugin.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the context in the plugin definition.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* If the requested context is not defined.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface.
|
||||
* The definition against which the context value must validate.
|
||||
*/
|
||||
public function getContextDefinition($name);
|
||||
|
||||
}
|
193
core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php
Normal file
193
core/lib/Drupal/Core/Plugin/DefaultLazyPluginCollection.php
Normal file
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\DefaultLazyPluginCollection.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Component\Plugin\LazyPluginCollection;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* Provides a default plugin collection for a plugin type.
|
||||
*
|
||||
* A plugin collection is used to contain plugins that will be lazily
|
||||
* instantiated. The configurations of each potential plugin are passed in, and
|
||||
* the configuration key containing the plugin ID is specified by
|
||||
* self::$pluginKey.
|
||||
*/
|
||||
class DefaultLazyPluginCollection extends LazyPluginCollection {
|
||||
use DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* The manager used to instantiate the plugins.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The initial configuration for each plugin in the collection.
|
||||
*
|
||||
* @var array
|
||||
* An associative array containing the initial configuration for each plugin
|
||||
* in the collection, keyed by plugin instance ID.
|
||||
*/
|
||||
protected $configurations = array();
|
||||
|
||||
/**
|
||||
* The key within the plugin configuration that contains the plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pluginKey = 'id';
|
||||
|
||||
/**
|
||||
* The original order of the instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $originalOrder = array();
|
||||
|
||||
/**
|
||||
* Constructs a new DefaultLazyPluginCollection object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
|
||||
* The manager to be used for instantiating plugins.
|
||||
* @param array $configurations
|
||||
* (optional) An associative array containing the initial configuration for
|
||||
* each plugin in the collection, keyed by plugin instance ID.
|
||||
*/
|
||||
public function __construct(PluginManagerInterface $manager, array $configurations = array()) {
|
||||
$this->manager = $manager;
|
||||
$this->configurations = $configurations;
|
||||
|
||||
if (!empty($configurations)) {
|
||||
$instance_ids = array_keys($configurations);
|
||||
$this->instanceIDs = array_combine($instance_ids, $instance_ids);
|
||||
// Store the original order of the instance IDs for export.
|
||||
$this->originalOrder = $this->instanceIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializePlugin($instance_id) {
|
||||
$configuration = isset($this->configurations[$instance_id]) ? $this->configurations[$instance_id] : array();
|
||||
if (!isset($configuration[$this->pluginKey])) {
|
||||
throw new PluginNotFoundException($instance_id);
|
||||
}
|
||||
$this->set($instance_id, $this->manager->createInstance($configuration[$this->pluginKey], $configuration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts all plugin instances in this collection.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function sort() {
|
||||
uasort($this->instanceIDs, array($this, 'sortHelper'));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides uasort() callback to sort plugins.
|
||||
*/
|
||||
public function sortHelper($aID, $bID) {
|
||||
$a = $this->get($aID);
|
||||
$b = $this->get($bID);
|
||||
return strnatcasecmp($a->getPluginId(), $b->getPluginId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration() {
|
||||
$instances = array();
|
||||
// Store the current order of the instances.
|
||||
$current_order = $this->instanceIDs;
|
||||
// Reorder the instances to match the original order, adding new instances
|
||||
// to the end.
|
||||
$this->instanceIDs = $this->originalOrder + $current_order;
|
||||
|
||||
foreach ($this as $instance_id => $instance) {
|
||||
if ($instance instanceof ConfigurablePluginInterface) {
|
||||
$instances[$instance_id] = $instance->getConfiguration();
|
||||
}
|
||||
else {
|
||||
$instances[$instance_id] = $this->configurations[$instance_id];
|
||||
}
|
||||
}
|
||||
// Restore the current order.
|
||||
$this->instanceIDs = $current_order;
|
||||
return $instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfiguration($configuration) {
|
||||
// Track each instance ID as it is updated.
|
||||
$unprocessed_instance_ids = $this->getInstanceIds();
|
||||
|
||||
foreach ($configuration as $instance_id => $instance_configuration) {
|
||||
$this->setInstanceConfiguration($instance_id, $instance_configuration);
|
||||
// Remove this instance ID from the list being updated.
|
||||
unset($unprocessed_instance_ids[$instance_id]);
|
||||
}
|
||||
|
||||
// Remove remaining instances that had no configuration specified for them.
|
||||
foreach ($unprocessed_instance_ids as $unprocessed_instance_id) {
|
||||
$this->removeInstanceId($unprocessed_instance_id);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the configuration for a plugin instance.
|
||||
*
|
||||
* If there is no plugin instance yet, a new will be instantiated. Otherwise,
|
||||
* the existing instance is updated with the new configuration.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The ID of a plugin to set the configuration for.
|
||||
* @param array $configuration
|
||||
* The plugin configuration to set.
|
||||
*/
|
||||
public function setInstanceConfiguration($instance_id, array $configuration) {
|
||||
$this->configurations[$instance_id] = $configuration;
|
||||
$instance = $this->get($instance_id);
|
||||
if ($instance instanceof ConfigurablePluginInterface) {
|
||||
$instance->setConfiguration($configuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addInstanceId($id, $configuration = NULL) {
|
||||
parent::addInstanceId($id);
|
||||
if ($configuration !== NULL) {
|
||||
$this->setInstanceConfiguration($id, $configuration);
|
||||
}
|
||||
if (!isset($this->originalOrder[$id])) {
|
||||
$this->originalOrder[$id] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeInstanceId($instance_id) {
|
||||
parent::removeInstanceId($instance_id);
|
||||
unset($this->originalOrder[$instance_id]);
|
||||
unset($this->configurations[$instance_id]);
|
||||
}
|
||||
|
||||
}
|
346
core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
Normal file
346
core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
Normal file
|
@ -0,0 +1,346 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\DefaultPluginManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
|
||||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
|
||||
use Drupal\Core\Plugin\Factory\ContainerFactory;
|
||||
|
||||
/**
|
||||
* Base class for plugin managers.
|
||||
*
|
||||
* @ingroup plugin_api
|
||||
*/
|
||||
class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface {
|
||||
|
||||
use DiscoveryCachedTrait;
|
||||
|
||||
/**
|
||||
* Cache backend instance.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* The cache key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheKey;
|
||||
|
||||
/**
|
||||
* An array of cache tags to use for the cached definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cacheTags = array();
|
||||
|
||||
/**
|
||||
* Name of the alter hook if one should be invoked.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $alterHook;
|
||||
|
||||
/**
|
||||
* The subdirectory within a namespace to look for plugins, or FALSE if the
|
||||
* plugins are in the top level of the namespace.
|
||||
*
|
||||
* @var string|bool
|
||||
*/
|
||||
protected $subdir;
|
||||
|
||||
/**
|
||||
* The module handler to invoke the alter hook.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* A set of defaults to be referenced by $this->processDefinition() if
|
||||
* additional processing of plugins is necessary or helpful for development
|
||||
* purposes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaults = array();
|
||||
|
||||
/**
|
||||
* Flag whether persistent caches should be used.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $useCaches = TRUE;
|
||||
|
||||
/**
|
||||
* The name of the annotation that contains the plugin definition.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pluginDefinitionAnnotationName;
|
||||
|
||||
/**
|
||||
* The interface each plugin should implement.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $pluginInterface;
|
||||
|
||||
/**
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
*
|
||||
* @var \Traversable
|
||||
*/
|
||||
protected $namespaces;
|
||||
|
||||
/**
|
||||
* Creates the discovery object.
|
||||
*
|
||||
* @param string|bool $subdir
|
||||
* The plugin's subdirectory, for example Plugin/views/filter.
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param string|null $plugin_interface
|
||||
* (optional) The interface each plugin should implement.
|
||||
* @param string $plugin_definition_annotation_name
|
||||
* (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_interface = NULL, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
|
||||
$this->subdir = $subdir;
|
||||
$this->namespaces = $namespaces;
|
||||
$this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
|
||||
$this->pluginInterface = $plugin_interface;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the cache backend.
|
||||
*
|
||||
* Plugin definitions are cached using the provided cache backend. The
|
||||
* interface language is added as a suffix to the cache key.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
* @param string $cache_key
|
||||
* Cache key prefix to use, the language code will be appended
|
||||
* automatically.
|
||||
* @param array $cache_tags
|
||||
* (optional) When providing a list of cache tags, the cached plugin
|
||||
* definitions are tagged with the provided cache tags. These cache tags can
|
||||
* then be used to clear the corresponding cached plugin definitions. Note
|
||||
* that this should be used with care! For clearing all cached plugin
|
||||
* definitions of a plugin manager, call that plugin manager's
|
||||
* clearCachedDefinitions() method. Only use cache tags when cached plugin
|
||||
* definitions should be cleared along with other, related cache entries.
|
||||
*/
|
||||
public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = array()) {
|
||||
Cache::validateTags($cache_tags);
|
||||
$this->cacheBackend = $cache_backend;
|
||||
$this->cacheKey = $cache_key;
|
||||
$this->cacheTags = $cache_tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the alter hook.
|
||||
*
|
||||
* @param string $alter_hook
|
||||
* Name of the alter hook; for example, to invoke
|
||||
* hook_mymodule_data_alter() pass in "mymodule_data".
|
||||
*/
|
||||
protected function alterInfo($alter_hook) {
|
||||
$this->alterHook = $alter_hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
$definitions = $this->getCachedDefinitions();
|
||||
if (!isset($definitions)) {
|
||||
$definitions = $this->findDefinitions();
|
||||
$this->setCachedDefinitions($definitions);
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearCachedDefinitions() {
|
||||
if ($this->cacheBackend) {
|
||||
if ($this->cacheTags) {
|
||||
// Use the cache tags to clear the cache.
|
||||
Cache::invalidateTags($this->cacheTags);
|
||||
}
|
||||
else {
|
||||
$this->cacheBackend->delete($this->cacheKey);
|
||||
}
|
||||
}
|
||||
$this->definitions = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached plugin definitions of the decorated discovery class.
|
||||
*
|
||||
* @return array|null
|
||||
* On success this will return an array of plugin definitions. On failure
|
||||
* this should return NULL, indicating to other methods that this has not
|
||||
* yet been defined. Success with no values should return as an empty array
|
||||
* and would actually be returned by the getDefinitions() method.
|
||||
*/
|
||||
protected function getCachedDefinitions() {
|
||||
if (!isset($this->definitions) && $cache = $this->cacheGet($this->cacheKey)) {
|
||||
$this->definitions = $cache->data;
|
||||
}
|
||||
return $this->definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a cache of plugin definitions for the decorated discovery class.
|
||||
*
|
||||
* @param array $definitions
|
||||
* List of definitions to store in cache.
|
||||
*/
|
||||
protected function setCachedDefinitions($definitions) {
|
||||
$this->cacheSet($this->cacheKey, $definitions, Cache::PERMANENT, $this->cacheTags);
|
||||
$this->definitions = $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function useCaches($use_caches = FALSE) {
|
||||
$this->useCaches = $use_caches;
|
||||
if (!$use_caches) {
|
||||
$this->definitions = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches from the cache backend, respecting the use caches flag.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::get()
|
||||
*/
|
||||
protected function cacheGet($cid) {
|
||||
if ($this->useCaches && $this->cacheBackend) {
|
||||
return $this->cacheBackend->get($cid);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores data in the persistent cache, respecting the use caches flag.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::set()
|
||||
*/
|
||||
protected function cacheSet($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
if ($this->cacheBackend && $this->useCaches) {
|
||||
$this->cacheBackend->set($cid, $data, $expire, $tags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs extra processing on plugin definitions.
|
||||
*
|
||||
* By default we add defaults for the type to the definition. If a type has
|
||||
* additional processing logic they can do that by replacing or extending the
|
||||
* method.
|
||||
*/
|
||||
public function processDefinition(&$definition, $plugin_id) {
|
||||
if (!empty($this->defaults) && is_array($this->defaults)) {
|
||||
$definition = NestedArray::mergeDeep($this->defaults, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDiscovery() {
|
||||
if (!$this->discovery) {
|
||||
$discovery = new AnnotatedClassDiscovery($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName);
|
||||
$this->discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
|
||||
}
|
||||
return $this->discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getFactory() {
|
||||
if (!$this->factory) {
|
||||
$this->factory = new ContainerFactory($this, $this->pluginInterface);
|
||||
}
|
||||
return $this->factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds plugin definitions.
|
||||
*
|
||||
* @return array
|
||||
* List of definitions to store in cache.
|
||||
*/
|
||||
protected function findDefinitions() {
|
||||
$definitions = $this->getDiscovery()->getDefinitions();
|
||||
foreach ($definitions as $plugin_id => &$definition) {
|
||||
$this->processDefinition($definition, $plugin_id);
|
||||
}
|
||||
$this->alterDefinitions($definitions);
|
||||
// If this plugin was provided by a module that does not exist, remove the
|
||||
// plugin definition.
|
||||
foreach ($definitions as $plugin_id => $plugin_definition) {
|
||||
// If the plugin definition is an object, attempt to convert it to an
|
||||
// array, if that is not possible, skip further processing.
|
||||
if (is_object($plugin_definition) && !($plugin_definition = (array) $plugin_definition)) {
|
||||
continue;
|
||||
}
|
||||
if (isset($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array('core', 'component')) && !$this->providerExists($plugin_definition['provider'])) {
|
||||
unset($definitions[$plugin_id]);
|
||||
}
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the hook to alter the definitions if the alter hook is set.
|
||||
*
|
||||
* @param $definitions
|
||||
* The discovered plugin defintions.
|
||||
*/
|
||||
protected function alterDefinitions(&$definitions) {
|
||||
if ($this->alterHook) {
|
||||
$this->moduleHandler->alter($this->alterHook, $definitions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the provider of a definition exists.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if provider exists, FALSE otherwise.
|
||||
*/
|
||||
protected function providerExists($provider) {
|
||||
return $this->moduleHandler->moduleExists($provider);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Component\Plugin\LazyPluginCollection;
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* Provides a default plugin collection for a plugin type.
|
||||
*
|
||||
* A plugin collection usually stores multiple plugins, and is used to lazily
|
||||
* instantiate them. When only one plugin is needed, it is still best practice
|
||||
* to encapsulate all of the instantiation logic in a plugin collection. This
|
||||
* class can be used directly, or subclassed to add further exception handling
|
||||
* in self::initializePlugin().
|
||||
*/
|
||||
class DefaultSingleLazyPluginCollection extends LazyPluginCollection {
|
||||
use DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* The manager used to instantiate the plugins.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* An array of configuration to instantiate the plugin with.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* The instance ID used for this plugin collection.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $instanceId;
|
||||
|
||||
/**
|
||||
* Constructs a new DefaultSingleLazyPluginCollection object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
|
||||
* The manager to be used for instantiating plugins.
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance.
|
||||
* @param array $configuration
|
||||
* An array of configuration.
|
||||
*/
|
||||
public function __construct(PluginManagerInterface $manager, $instance_id, array $configuration) {
|
||||
$this->manager = $manager;
|
||||
$this->instanceId = $instance_id;
|
||||
// This is still needed by the parent LazyPluginCollection class.
|
||||
$this->instanceIDs = array($instance_id => $instance_id);
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializePlugin($instance_id) {
|
||||
$this->set($instance_id, $this->manager->createInstance($instance_id, $this->configuration));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration() {
|
||||
$plugin = $this->get($this->instanceId);
|
||||
if ($plugin instanceof ConfigurablePluginInterface) {
|
||||
return $plugin->getConfiguration();
|
||||
}
|
||||
else {
|
||||
return $this->configuration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfiguration($configuration) {
|
||||
$plugin = $this->get($this->instanceId);
|
||||
if ($plugin instanceof ConfigurablePluginInterface) {
|
||||
$plugin->setConfiguration($configuration);
|
||||
}
|
||||
$this->configuration = $configuration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addInstanceId($id, $configuration = NULL) {
|
||||
parent::addInstanceId($id, $configuration);
|
||||
if ($configuration !== NULL) {
|
||||
$this->setConfiguration($configuration);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Annotation\AnnotationInterface;
|
||||
use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery as ComponentAnnotatedClassDiscovery;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
/**
|
||||
* Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces.
|
||||
*/
|
||||
class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
|
||||
|
||||
/**
|
||||
* A suffix to append to each PSR-4 directory associated with a base
|
||||
* namespace, to form the directories where plugins are found.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $directorySuffix = '';
|
||||
|
||||
/**
|
||||
* A suffix to append to each base namespace, to obtain the namespaces where
|
||||
* plugins are found.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespaceSuffix = '';
|
||||
|
||||
/**
|
||||
* A list of base namespaces with their PSR-4 directories.
|
||||
*
|
||||
* @var \Traversable
|
||||
*/
|
||||
protected $rootNamespacesIterator;
|
||||
|
||||
/**
|
||||
* Constructs an AnnotatedClassDiscovery object.
|
||||
*
|
||||
* @param string $subdir
|
||||
* Either the plugin's subdirectory, for example 'Plugin/views/filter', or
|
||||
* empty string if plugins are located at the top level of the namespace.
|
||||
* @param \Traversable $root_namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* If $subdir is not an empty string, it will be appended to each namespace.
|
||||
* @param string $plugin_definition_annotation_name
|
||||
* (optional) The name of the annotation that contains the plugin definition.
|
||||
* Defaults to 'Drupal\Component\Annotation\Plugin'.
|
||||
*/
|
||||
function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
|
||||
if ($subdir) {
|
||||
// Prepend a directory separator to $subdir,
|
||||
// if it does not already have one.
|
||||
if ('/' !== $subdir[0]) {
|
||||
$subdir = '/' . $subdir;
|
||||
}
|
||||
$this->directorySuffix = $subdir;
|
||||
$this->namespaceSuffix = str_replace('/', '\\', $subdir);
|
||||
}
|
||||
$this->rootNamespacesIterator = $root_namespaces;
|
||||
$plugin_namespaces = array();
|
||||
parent::__construct($plugin_namespaces, $plugin_definition_annotation_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAnnotationReader() {
|
||||
if (!isset($this->annotationReader)) {
|
||||
$reader = parent::getAnnotationReader();
|
||||
|
||||
// Add the Core annotation classes like @Translation.
|
||||
$reader->addNamespace('Drupal\Core\Annotation', array(dirname(dirname(__DIR__)) . '/Annotation'));
|
||||
$this->annotationReader = $reader;
|
||||
}
|
||||
return $this->annotationReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
|
||||
parent::prepareAnnotationDefinition($annotation, $class);
|
||||
|
||||
if (!$annotation->getProvider()) {
|
||||
$annotation->setProvider($this->getProviderFromNamespace($class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the provider name from a Drupal namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* The namespace to extract the provider from.
|
||||
*
|
||||
* @return string|null
|
||||
* The matching provider name, or NULL otherwise.
|
||||
*/
|
||||
protected function getProviderFromNamespace($namespace) {
|
||||
preg_match('|^Drupal\\\\(?<provider>[\w]+)\\\\|', $namespace, $matches);
|
||||
|
||||
if (isset($matches['provider'])) {
|
||||
return Unicode::strtolower($matches['provider']);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getPluginNamespaces() {
|
||||
$plugin_namespaces = array();
|
||||
if ($this->namespaceSuffix) {
|
||||
foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
|
||||
// Append the namespace suffix to the base namespace, to obtain the
|
||||
// plugin namespace. E.g. 'Drupal\Views' may become
|
||||
// 'Drupal\Views\Plugin\Block'.
|
||||
$namespace .= $this->namespaceSuffix;
|
||||
foreach ((array) $dirs as $dir) {
|
||||
// Append the directory suffix to the PSR-4 base directory, to obtain
|
||||
// the directory where plugins are found.
|
||||
// E.g. DRUPAL_ROOT . '/core/modules/views/src' may become
|
||||
// DRUPAL_ROOT . '/core/modules/views/src/Plugin/Block'.
|
||||
$plugin_namespaces[$namespace][] = $dir . $this->directorySuffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Both the namespace suffix and the directory suffix are empty,
|
||||
// so the plugin namespaces and directories are the same as the base
|
||||
// directories.
|
||||
foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
|
||||
$plugin_namespaces[$namespace] = (array) $dirs;
|
||||
}
|
||||
}
|
||||
|
||||
return $plugin_namespaces;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
|
||||
|
||||
class ContainerDerivativeDiscoveryDecorator extends DerivativeDiscoveryDecorator {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDeriver($base_plugin_id, $base_definition) {
|
||||
if (!isset($this->derivers[$base_plugin_id])) {
|
||||
$this->derivers[$base_plugin_id] = FALSE;
|
||||
$class = $this->getDeriverClass($base_definition);
|
||||
if ($class) {
|
||||
// If the deriver provides a factory method, pass the container to it.
|
||||
if (is_subclass_of($class, '\Drupal\Core\Plugin\Discovery\ContainerDeriverInterface')) {
|
||||
/** @var \Drupal\Core\Plugin\Discovery\ContainerDeriverInterface $class */
|
||||
$this->derivers[$base_plugin_id] = $class::create(\Drupal::getContainer(), $base_plugin_id);
|
||||
}
|
||||
else {
|
||||
$this->derivers[$base_plugin_id] = new $class($base_plugin_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->derivers[$base_plugin_id] ?: NULL;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\ContainerDeriverInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides additional plugin definitions based on an existing definition using
|
||||
* service injection.
|
||||
*/
|
||||
interface ContainerDeriverInterface extends DeriverInterface {
|
||||
|
||||
/**
|
||||
* Creates a new class instance.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The container to pull out services used in the fetcher.
|
||||
* @param string $base_plugin_id
|
||||
* The base plugin ID for the plugin ID.
|
||||
*
|
||||
* @return static
|
||||
* Returns an instance of this fetcher.
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id);
|
||||
|
||||
}
|
63
core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php
Normal file
63
core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\HookDiscovery.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
|
||||
/**
|
||||
* Provides a hook-based plugin discovery class.
|
||||
*/
|
||||
class HookDiscovery implements DiscoveryInterface {
|
||||
|
||||
use DiscoveryTrait;
|
||||
|
||||
/**
|
||||
* The name of the hook that will be implemented by this discovery instance.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $hook;
|
||||
|
||||
/**
|
||||
* The module handler used to find and execute the plugin hook.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs a Drupal\Core\Plugin\Discovery\HookDiscovery object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param string $hook
|
||||
* The Drupal hook that a module can implement in order to interface to
|
||||
* this discovery class.
|
||||
*/
|
||||
function __construct(ModuleHandlerInterface $module_handler, $hook) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->hook = $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
$definitions = array();
|
||||
foreach ($this->moduleHandler->getImplementations($this->hook) as $module) {
|
||||
$result = $this->moduleHandler->invoke($module, $this->hook);
|
||||
foreach ($result as $plugin_id => $definition) {
|
||||
$definition['provider'] = $module;
|
||||
$definitions[$plugin_id] = $definition;
|
||||
}
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
}
|
66
core/lib/Drupal/Core/Plugin/Discovery/InfoHookDecorator.php
Normal file
66
core/lib/Drupal/Core/Plugin/Discovery/InfoHookDecorator.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\InfoHookDecorator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
|
||||
|
||||
/**
|
||||
* Allows info hook implementations to enhance discovered plugin definitions.
|
||||
*/
|
||||
class InfoHookDecorator implements DiscoveryInterface {
|
||||
|
||||
use DiscoveryTrait;
|
||||
|
||||
/**
|
||||
* The Discovery object being decorated.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
|
||||
*/
|
||||
protected $decorated;
|
||||
|
||||
/**
|
||||
* The name of the info hook that will be implemented by this discovery instance.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $hook;
|
||||
|
||||
/**
|
||||
* Constructs a InfoHookDecorator object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
|
||||
* The object implementing DiscoveryInterface that is being decorated.
|
||||
* @param string $hook
|
||||
* The name of the info hook to be invoked by this discovery instance.
|
||||
*/
|
||||
public function __construct(DiscoveryInterface $decorated, $hook) {
|
||||
$this->decorated = $decorated;
|
||||
$this->hook = $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
$definitions = $this->decorated->getDefinitions();
|
||||
foreach (\Drupal::moduleHandler()->getImplementations($this->hook) as $module) {
|
||||
$function = $module . '_' . $this->hook;
|
||||
$function($definitions);
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through all unknown calls onto the decorated object.
|
||||
*/
|
||||
public function __call($method, $args) {
|
||||
return call_user_func_array(array($this->decorated, $method), $args);
|
||||
}
|
||||
|
||||
}
|
60
core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php
Normal file
60
core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\YamlDiscovery.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
|
||||
use Drupal\Component\Discovery\YamlDiscovery as ComponentYamlDiscovery;
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
|
||||
|
||||
/**
|
||||
* Allows YAML files to define plugin definitions.
|
||||
*/
|
||||
class YamlDiscovery implements DiscoveryInterface {
|
||||
|
||||
use DiscoveryTrait;
|
||||
|
||||
/**
|
||||
* YAML file discovery and parsing handler.
|
||||
*
|
||||
* @var \Drupal\Component\Discovery\YamlDiscovery
|
||||
*/
|
||||
protected $discovery;
|
||||
|
||||
/**
|
||||
* Construct a YamlDiscovery object.
|
||||
*
|
||||
* @param string $name
|
||||
* The file name suffix to use for discovery. E.g. 'test' will become
|
||||
* 'MODULE.test.yml'.
|
||||
* @param array $directories
|
||||
* An array of directories to scan.
|
||||
*/
|
||||
function __construct($name, array $directories) {
|
||||
$this->discovery = new ComponentYamlDiscovery($name, $directories);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
$plugins = $this->discovery->findAll();
|
||||
|
||||
// Flatten definitions into what's expected from plugins.
|
||||
$definitions = array();
|
||||
foreach ($plugins as $provider => $list) {
|
||||
foreach ($list as $id => $definition) {
|
||||
$definitions[$id] = $definition + array(
|
||||
'provider' => $provider,
|
||||
'id' => $id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
|
||||
|
||||
/**
|
||||
* Enables YAML discovery for plugin definitions.
|
||||
*
|
||||
* You should normally extend this class to add validation for the values in the
|
||||
* YAML data or to restrict use of the class or derivatives keys.
|
||||
*/
|
||||
class YamlDiscoveryDecorator extends YamlDiscovery {
|
||||
|
||||
/**
|
||||
* The Discovery object being decorated.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
|
||||
*/
|
||||
protected $decorated;
|
||||
|
||||
/**
|
||||
* Constructs a YamlDiscoveryDecorator object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
|
||||
* The discovery object that is being decorated.
|
||||
* @param string $name
|
||||
* The file name suffix to use for discovery. E.g. 'test' will become
|
||||
* 'MODULE.test.yml'.
|
||||
* @param array $directories
|
||||
* An array of directories to scan.
|
||||
*/
|
||||
public function __construct(DiscoveryInterface $decorated, $name, array $directories) {
|
||||
parent::__construct($name, $directories);
|
||||
|
||||
$this->decorated = $decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
return parent::getDefinitions() + $this->decorated->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through all unknown calls onto the decorated object.
|
||||
*/
|
||||
public function __call($method, $args) {
|
||||
return call_user_func_array(array($this->decorated, $method), $args);
|
||||
}
|
||||
|
||||
}
|
32
core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php
Normal file
32
core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Factory\ContainerFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin\Factory;
|
||||
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
|
||||
/**
|
||||
* Plugin factory which passes a container to a create method.
|
||||
*/
|
||||
class ContainerFactory extends DefaultFactory {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createInstance($plugin_id, array $configuration = array()) {
|
||||
$plugin_definition = $this->discovery->getDefinition($plugin_id);
|
||||
$plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
|
||||
|
||||
// If the plugin provides a factory method, pass the container to it.
|
||||
if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
|
||||
return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
|
||||
}
|
||||
|
||||
// Otherwise, create the plugin directly.
|
||||
return new $plugin_class($configuration, $plugin_id, $plugin_definition);
|
||||
}
|
||||
|
||||
}
|
23
core/lib/Drupal/Core/Plugin/PluginBase.php
Normal file
23
core/lib/Drupal/Core/Plugin/PluginBase.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\PluginBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBase as ComponentPluginBase;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* Base class for plugins supporting metadata inspection and translation.
|
||||
*
|
||||
* @ingroup plugin_api
|
||||
*/
|
||||
abstract class PluginBase extends ComponentPluginBase {
|
||||
use StringTranslationTrait;
|
||||
use DependencySerializationTrait;
|
||||
|
||||
}
|
45
core/lib/Drupal/Core/Plugin/PluginDependencyTrait.php
Normal file
45
core/lib/Drupal/Core/Plugin/PluginDependencyTrait.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\PluginDependencyTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Core\Entity\DependencyTrait;
|
||||
|
||||
/**
|
||||
* Provides a trait for calculating the dependencies of a plugin.
|
||||
*/
|
||||
trait PluginDependencyTrait {
|
||||
|
||||
use DependencyTrait;
|
||||
|
||||
/**
|
||||
* Calculates and adds dependencies of a specific plugin instance.
|
||||
*
|
||||
* Dependencies are added for the module that provides the plugin, as well
|
||||
* as any dependencies declared by the instance's calculateDependencies()
|
||||
* method, if it implements
|
||||
* \Drupal\Component\Plugin\DependentPluginInterface.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginInspectionInterface $instance
|
||||
* The plugin instance.
|
||||
*/
|
||||
protected function calculatePluginDependencies(PluginInspectionInterface $instance) {
|
||||
$definition = $instance->getPluginDefinition();
|
||||
$this->addDependency('module', $definition['provider']);
|
||||
// Plugins can declare additional dependencies in their definition.
|
||||
if (isset($definition['config_dependencies'])) {
|
||||
$this->addDependencies($definition['config_dependencies']);
|
||||
}
|
||||
// If a plugin is dependent, calculate its dependencies.
|
||||
if ($instance instanceof DependentPluginInterface && $plugin_dependencies = $instance->calculateDependencies()) {
|
||||
$this->addDependencies($plugin_dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
63
core/lib/Drupal/Core/Plugin/PluginFormInterface.php
Normal file
63
core/lib/Drupal/Core/Plugin/PluginFormInterface.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\PluginFormInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for a plugin that contains a form.
|
||||
*
|
||||
* Plugin forms are usually contained in other forms. In order to know where the
|
||||
* plugin form is located in the parent form, #parents and #array_parents must
|
||||
* be known, but these are not available during the initial build phase. In
|
||||
* order to have these properties available when building the plugin form's
|
||||
* elements, let buildConfigurationForm() return a form element that has a
|
||||
* #process callback and build the rest of the form in the callback. By the time
|
||||
* the callback is executed, the element's #parents and #array_parents
|
||||
* properties will have been set by the form API. For more documentation on
|
||||
* #parents and #array_parents, see
|
||||
* https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/8.
|
||||
*
|
||||
* @ingroup plugin_api
|
||||
*/
|
||||
interface PluginFormInterface {
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state);
|
||||
|
||||
/**
|
||||
* Form validation handler.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state);
|
||||
|
||||
/**
|
||||
* Form submission handler.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state);
|
||||
|
||||
}
|
33
core/lib/Drupal/Core/Plugin/PluginManagerPass.php
Normal file
33
core/lib/Drupal/Core/Plugin/PluginManagerPass.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\PluginManagerPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Plugin;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Registers plugin managers to the plugin.cache_clearer service.
|
||||
*/
|
||||
class PluginManagerPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$cache_clearer_definition = $container->getDefinition('plugin.cache_clearer');
|
||||
foreach ($container->getDefinitions() as $service_id => $definition) {
|
||||
if (strpos($service_id, 'plugin.manager.') === 0 || $definition->hasTag('plugin_manager_cache_clear')) {
|
||||
if (is_subclass_of($definition->getClass(), '\Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface')) {
|
||||
$cache_clearer_definition->addMethodCall('addCachedDiscovery', array(new Reference($service_id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue