Commit 1b66e536 authored by larowlan's avatar larowlan

Issue #2961822 by phenaproxima, tim.plunkett: Support object-based plugin...

Issue #2961822 by phenaproxima, tim.plunkett: Support object-based plugin definitions in ContextHandler
parent 41c3a88e
<?php
namespace Drupal\Component\Plugin\Definition;
use Drupal\Component\Plugin\Context\ContextDefinitionInterface;
/**
* Provides an interface for plugin definitions which use contexts.
*
* @ingroup Plugin
*/
interface ContextAwarePluginDefinitionInterface extends PluginDefinitionInterface {
/**
* Checks if the plugin defines a particular context.
*
* @param string $name
* The context name.
*
* @return bool
* TRUE if the plugin defines the given context, otherwise FALSE.
*/
public function hasContextDefinition($name);
/**
* Returns all context definitions for this plugin.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]
* The context definitions.
*/
public function getContextDefinitions();
/**
* Returns a particular context definition for this plugin.
*
* @param string $name
* The context name.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface
* The context definition.
*
* @throws \Drupal\Component\Plugin\Exception\ContextException
* Thrown if the plugin does not define the given context.
*/
public function getContextDefinition($name);
/**
* Adds a context to this plugin definition.
*
* @param string $name
* The context name.
* @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $definition
* The context definition.
*
* @return $this
* The called object.
*/
public function addContextDefinition($name, ContextDefinitionInterface $definition);
/**
* Removes a context definition from this plugin.
*
* @param string $name
* The context name.
*
* @return $this
* The called object.
*/
public function removeContextDefinition($name);
}
<?php
namespace Drupal\Component\Plugin\Definition;
use Drupal\Component\Plugin\Context\ContextDefinitionInterface;
use Drupal\Component\Plugin\Exception\ContextException;
/**
* Provides a trait for context-aware object-based plugin definitions.
*/
trait ContextAwarePluginDefinitionTrait {
/**
* The context definitions for this plugin definition.
*
* @var \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]
*/
protected $contextDefinitions = [];
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::hasContextDefinition().
*/
public function hasContextDefinition($name) {
return array_key_exists($name, $this->contextDefinitions);
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::getContextDefinitions().
*/
public function getContextDefinitions() {
return $this->contextDefinitions;
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::getContextDefinition().
*/
public function getContextDefinition($name) {
if ($this->hasContextDefinition($name)) {
return $this->contextDefinitions[$name];
}
throw new ContextException($this->id() . " does not define a '$name' context");
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::addContextDefinition().
*/
public function addContextDefinition($name, ContextDefinitionInterface $definition) {
$this->contextDefinitions[$name] = $definition;
return $this;
}
/**
* Implements \Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface::removeContextDefinition().
*/
public function removeContextDefinition($name) {
unset($this->contextDefinitions[$name]);
return $this;
}
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\Core\Plugin\Context; namespace Drupal\Core\Plugin\Context;
use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Component\Plugin\Exception\MissingValueContextException; use Drupal\Component\Plugin\Exception\MissingValueContextException;
use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableDependencyInterface;
...@@ -17,18 +18,37 @@ class ContextHandler implements ContextHandlerInterface { ...@@ -17,18 +18,37 @@ class ContextHandler implements ContextHandlerInterface {
*/ */
public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) { public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
return array_filter($definitions, function ($plugin_definition) use ($contexts) { return array_filter($definitions, function ($plugin_definition) use ($contexts) {
// If this plugin doesn't need any context, it is available to use. $context_definitions = $this->getContextDefinitions($plugin_definition);
// @todo Support object-based plugin definitions in
// https://www.drupal.org/project/drupal/issues/2961822.
if (!is_array($plugin_definition) || !isset($plugin_definition['context'])) {
return TRUE;
}
// Check the set of contexts against the requirements. if ($context_definitions) {
return $this->checkRequirements($contexts, $plugin_definition['context']); // Check the set of contexts against the requirements.
return $this->checkRequirements($contexts, $context_definitions);
}
// If this plugin doesn't need any context, it is available to use.
return TRUE;
}); });
} }
/**
* Returns the context definitions associated with a plugin definition.
*
* @param array|\Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface $plugin_definition
* The plugin definition.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]|null
* The context definitions, or NULL if the plugin definition does not
* support contexts.
*/
protected function getContextDefinitions($plugin_definition) {
if ($plugin_definition instanceof ContextAwarePluginDefinitionInterface) {
return $plugin_definition->getContextDefinitions();
}
if (is_array($plugin_definition) && isset($plugin_definition['context'])) {
return $plugin_definition['context'];
}
return NULL;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
namespace Drupal\Tests\Core\Plugin; namespace Drupal\Tests\Core\Plugin;
use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionTrait;
use Drupal\Component\Plugin\Definition\PluginDefinition;
use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Component\Plugin\Exception\MissingValueContextException; use Drupal\Component\Plugin\Exception\MissingValueContextException;
use Drupal\Core\Cache\NullBackend; use Drupal\Core\Cache\NullBackend;
...@@ -209,49 +212,100 @@ public function providerTestFilterPluginDefinitionsByContexts() { ...@@ -209,49 +212,100 @@ public function providerTestFilterPluginDefinitionsByContexts() {
// No context and no plugins, no plugins available. // No context and no plugins, no plugins available.
$data[] = [FALSE, $plugins, []]; $data[] = [FALSE, $plugins, []];
$plugins = ['expected_plugin' => []]; $plugins = [
'expected_array_plugin' => [],
'expected_object_plugin' => new ContextAwarePluginDefinition(),
];
// No context, all plugins available. // No context, all plugins available.
$data[] = [FALSE, $plugins, $plugins]; $data[] = [FALSE, $plugins, $plugins];
$plugins = ['expected_plugin' => ['context' => []]]; $plugins = [
'expected_array_plugin' => ['context' => []],
'expected_object_plugin' => new ContextAwarePluginDefinition(),
];
// No context, all plugins available. // No context, all plugins available.
$data[] = [FALSE, $plugins, $plugins]; $data[] = [FALSE, $plugins, $plugins];
$plugins = ['expected_plugin' => ['context' => ['context1' => new ContextDefinition('string')]]]; $plugins = [
'expected_array_plugin' => [
'context' => ['context1' => new ContextDefinition('string')],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', new ContextDefinition('string')),
];
// Missing context, no plugins available. // Missing context, no plugins available.
$data[] = [FALSE, $plugins, []]; $data[] = [FALSE, $plugins, []];
// Satisfied context, all plugins available. // Satisfied context, all plugins available.
$data[] = [TRUE, $plugins, $plugins]; $data[] = [TRUE, $plugins, $plugins];
$mismatched_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']); $mismatched_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']);
$plugins = ['expected_plugin' => ['context' => ['context1' => $mismatched_context_definition]]]; $plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $mismatched_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $mismatched_context_definition),
];
// Mismatched constraints, no plugins available. // Mismatched constraints, no plugins available.
$data[] = [TRUE, $plugins, []]; $data[] = [TRUE, $plugins, []];
$optional_mismatched_context_definition = clone $mismatched_context_definition; $optional_mismatched_context_definition = clone $mismatched_context_definition;
$optional_mismatched_context_definition->setRequired(FALSE); $optional_mismatched_context_definition->setRequired(FALSE);
$plugins = ['expected_plugin' => ['context' => ['context1' => $optional_mismatched_context_definition]]]; $plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $optional_mismatched_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $optional_mismatched_context_definition),
];
// Optional mismatched constraint, all plugins available. // Optional mismatched constraint, all plugins available.
$data[] = [FALSE, $plugins, $plugins]; $data[] = [FALSE, $plugins, $plugins];
$expected_context_definition = (new ContextDefinition('string'))->setConstraints(['Blank' => []]); $expected_context_definition = (new ContextDefinition('string'))->setConstraints(['Blank' => []]);
$plugins = ['expected_plugin' => ['context' => ['context1' => $expected_context_definition]]]; $plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $expected_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $expected_context_definition),
];
// Satisfied context with constraint, all plugins available. // Satisfied context with constraint, all plugins available.
$data[] = [TRUE, $plugins, $plugins]; $data[] = [TRUE, $plugins, $plugins];
$optional_expected_context_definition = clone $expected_context_definition; $optional_expected_context_definition = clone $expected_context_definition;
$optional_expected_context_definition->setRequired(FALSE); $optional_expected_context_definition->setRequired(FALSE);
$plugins = ['expected_plugin' => ['context' => ['context1' => $optional_expected_context_definition]]]; $plugins = [
'expected_array_plugin' => [
'context' => ['context1' => $optional_expected_context_definition],
],
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $optional_expected_context_definition),
];
// Optional unsatisfied context, all plugins available. // Optional unsatisfied context, all plugins available.
$data[] = [FALSE, $plugins, $plugins]; $data[] = [FALSE, $plugins, $plugins];
$unexpected_context_definition = (new ContextDefinition('unexpected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']); $unexpected_context_definition = (new ContextDefinition('unexpected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']);
$plugins = [ $plugins = [
'unexpected_plugin' => ['context' => ['context1' => $unexpected_context_definition]], 'unexpected_array_plugin' => [
'expected_plugin' => ['context' => ['context2' => new ContextDefinition('string')]], 'context' => ['context1' => $unexpected_context_definition],
],
'expected_array_plugin' => [
'context' => ['context2' => new ContextDefinition('string')],
],
'unexpected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context1', $unexpected_context_definition),
'expected_object_plugin' => (new ContextAwarePluginDefinition())
->addContextDefinition('context2', new ContextDefinition('string')),
];
// Context only satisfies two plugins.
$data[] = [
TRUE,
$plugins,
[
'expected_array_plugin' => $plugins['expected_array_plugin'],
'expected_object_plugin' => $plugins['expected_object_plugin'],
],
]; ];
// Context only satisfies one plugin.
$data[] = [TRUE, $plugins, ['expected_plugin' => $plugins['expected_plugin']]];
return $data; return $data;
} }
...@@ -517,4 +571,10 @@ public function testApplyContextMappingConfigurableAssignedMiss() { ...@@ -517,4 +571,10 @@ public function testApplyContextMappingConfigurableAssignedMiss() {
} }
interface TestConfigurableContextAwarePluginInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface { interface TestConfigurableContextAwarePluginInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface {
}
class ContextAwarePluginDefinition extends PluginDefinition implements ContextAwarePluginDefinitionInterface {
use ContextAwarePluginDefinitionTrait;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment