Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View 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();
}
}
}

View 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;
}
}

View file

@ -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);
}

View 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();
}
}

View file

@ -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());
}

View file

@ -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();
}

View 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;
}
}

View file

@ -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();
}

View 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))]));
}
}
}

View file

@ -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());
}

View 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);
}

View file

@ -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;
}
}

View 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');
}
}

View 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);
}

View 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]);
}
}

View 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);
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View file

@ -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);
}
}

View 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);
}
}

View 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;
}

View 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);
}
}
}

View 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);
}

View 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)));
}
}
}
}
}