3 namespace Drupal\Core\Plugin\Context;
5 use Drupal\Component\Plugin\Exception\ContextException;
6 use Drupal\Core\Cache\CacheableDependencyInterface;
7 use Drupal\Core\Plugin\ContextAwarePluginInterface;
10 * Provides methods to handle sets of contexts.
12 class ContextHandler implements ContextHandlerInterface {
17 public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
18 return array_filter($definitions, function ($plugin_definition) use ($contexts) {
19 // If this plugin doesn't need any context, it is available to use.
20 if (!isset($plugin_definition['context'])) {
24 // Check the set of contexts against the requirements.
25 return $this->checkRequirements($contexts, $plugin_definition['context']);
32 public function checkRequirements(array $contexts, array $requirements) {
33 foreach ($requirements as $requirement) {
34 if ($requirement->isRequired() && !$this->getMatchingContexts($contexts, $requirement)) {
44 public function getMatchingContexts(array $contexts, ContextDefinitionInterface $definition) {
45 return array_filter($contexts, function (ContextInterface $context) use ($definition) {
46 return $definition->isSatisfiedBy($context);
53 public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = []) {
54 /** @var $contexts \Drupal\Core\Plugin\Context\ContextInterface[] */
55 $mappings += $plugin->getContextMapping();
56 // Loop through each of the expected contexts.
60 foreach ($plugin->getContextDefinitions() as $plugin_context_id => $plugin_context_definition) {
61 // If this context was given a specific name, use that.
62 $context_id = isset($mappings[$plugin_context_id]) ? $mappings[$plugin_context_id] : $plugin_context_id;
63 if (!empty($contexts[$context_id])) {
64 // This assignment has been used, remove it.
65 unset($mappings[$plugin_context_id]);
67 // Plugins have their on context objects, only the value is applied.
68 // They also need to know about the cacheability metadata of where that
69 // value is coming from, so pass them through to those objects.
70 $plugin_context = $plugin->getContext($plugin_context_id);
71 if ($plugin_context instanceof ContextInterface && $contexts[$context_id] instanceof CacheableDependencyInterface) {
72 $plugin_context->addCacheableDependency($contexts[$context_id]);
75 // Pass the value to the plugin if there is one.
76 if ($contexts[$context_id]->hasContextValue()) {
77 $plugin->setContextValue($plugin_context_id, $contexts[$context_id]->getContextData());
79 elseif ($plugin_context_definition->isRequired()) {
80 // Collect required contexts that exist but are missing a value.
81 $missing_value[] = $plugin_context_id;
84 elseif ($plugin_context_definition->isRequired()) {
85 // Collect required contexts that are missing.
86 $missing_value[] = $plugin_context_id;
89 // Ignore mappings for optional missing context.
90 unset($mappings[$plugin_context_id]);
94 // If there are any required contexts without a value, throw an exception.
96 throw new ContextException(sprintf('Required contexts without a value: %s.', implode(', ', $missing_value)));
99 // If there are any mappings that were not satisfied, throw an exception.
100 if (!empty($mappings)) {
101 throw new ContextException('Assigned contexts were not satisfied: ' . implode(',', array_keys($mappings)));