3 namespace Drupal\Core\Plugin\Context;
5 use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
6 use Drupal\Component\Plugin\Exception\ContextException;
7 use Drupal\Component\Plugin\Exception\MissingValueContextException;
8 use Drupal\Core\Cache\CacheableDependencyInterface;
9 use Drupal\Core\Plugin\ContextAwarePluginInterface;
12 * Provides methods to handle sets of contexts.
14 class ContextHandler implements ContextHandlerInterface {
19 public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
20 return array_filter($definitions, function ($plugin_definition) use ($contexts) {
21 $context_definitions = $this->getContextDefinitions($plugin_definition);
23 if ($context_definitions) {
24 // Check the set of contexts against the requirements.
25 return $this->checkRequirements($contexts, $context_definitions);
27 // If this plugin doesn't need any context, it is available to use.
33 * Returns the context definitions associated with a plugin definition.
35 * @param array|\Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface $plugin_definition
36 * The plugin definition.
38 * @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]|null
39 * The context definitions, or NULL if the plugin definition does not
42 protected function getContextDefinitions($plugin_definition) {
43 if ($plugin_definition instanceof ContextAwarePluginDefinitionInterface) {
44 return $plugin_definition->getContextDefinitions();
46 if (is_array($plugin_definition) && isset($plugin_definition['context'])) {
47 return $plugin_definition['context'];
55 public function checkRequirements(array $contexts, array $requirements) {
56 foreach ($requirements as $requirement) {
57 if ($requirement->isRequired() && !$this->getMatchingContexts($contexts, $requirement)) {
67 public function getMatchingContexts(array $contexts, ContextDefinitionInterface $definition) {
68 return array_filter($contexts, function (ContextInterface $context) use ($definition) {
69 return $definition->isSatisfiedBy($context);
76 public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = []) {
77 /** @var $contexts \Drupal\Core\Plugin\Context\ContextInterface[] */
78 $mappings += $plugin->getContextMapping();
79 // Loop through each of the expected contexts.
83 foreach ($plugin->getContextDefinitions() as $plugin_context_id => $plugin_context_definition) {
84 // If this context was given a specific name, use that.
85 $context_id = isset($mappings[$plugin_context_id]) ? $mappings[$plugin_context_id] : $plugin_context_id;
86 if (!empty($contexts[$context_id])) {
87 // This assignment has been used, remove it.
88 unset($mappings[$plugin_context_id]);
90 // Plugins have their on context objects, only the value is applied.
91 // They also need to know about the cacheability metadata of where that
92 // value is coming from, so pass them through to those objects.
93 $plugin_context = $plugin->getContext($plugin_context_id);
94 if ($plugin_context instanceof ContextInterface && $contexts[$context_id] instanceof CacheableDependencyInterface) {
95 $plugin_context->addCacheableDependency($contexts[$context_id]);
98 // Pass the value to the plugin if there is one.
99 if ($contexts[$context_id]->hasContextValue()) {
100 $plugin->setContextValue($plugin_context_id, $contexts[$context_id]->getContextData());
102 elseif ($plugin_context_definition->isRequired()) {
103 // Collect required contexts that exist but are missing a value.
104 $missing_value[] = $plugin_context_id;
107 elseif ($plugin_context_definition->isRequired()) {
108 // Collect required contexts that are missing.
109 $missing_value[] = $plugin_context_id;
112 // Ignore mappings for optional missing context.
113 unset($mappings[$plugin_context_id]);
117 // If there are any mappings that were not satisfied, throw an exception.
118 // This is a more severe problem than missing values, so check and throw
120 if (!empty($mappings)) {
121 throw new ContextException('Assigned contexts were not satisfied: ' . implode(',', array_keys($mappings)));
124 // If there are any required contexts without a value, throw an exception.
125 if ($missing_value) {
126 throw new MissingValueContextException($missing_value);