Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
60.00% |
6 / 10 |
CRAP | |
92.16% |
47 / 51 |
DerivativeDiscoveryDecorator | |
0.00% |
0 / 1 |
|
60.00% |
6 / 10 |
32.49 | |
92.16% |
47 / 51 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
getDefinition | |
0.00% |
0 / 1 |
5.02 | |
90.91% |
10 / 11 |
|||
getDefinitions | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
getDerivatives | |
100.00% |
1 / 1 |
7 | |
100.00% |
13 / 13 |
|||
decodePluginId | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
|||
encodePluginId | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
|||
getDeriver | |
100.00% |
1 / 1 |
4 | |
100.00% |
6 / 6 |
|||
getDeriverClass | |
100.00% |
1 / 1 |
7 | |
100.00% |
7 / 7 |
|||
mergeDerivativeDefinition | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
__call | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator. | |
*/ | |
namespace Drupal\Component\Plugin\Discovery; | |
use Drupal\Component\Plugin\Exception\InvalidDeriverException; | |
/** | |
* Base class providing the tools for a plugin discovery to be derivative aware. | |
* | |
* Provides a decorator that allows the use of plugin derivatives for normal | |
* implementations DiscoveryInterface. | |
*/ | |
class DerivativeDiscoveryDecorator implements DiscoveryInterface { | |
use DiscoveryTrait; | |
/** | |
* Plugin derivers. | |
* | |
* @var \Drupal\Component\Plugin\Derivative\DeriverInterface[] | |
* Keys are base plugin IDs. | |
*/ | |
protected $derivers = array(); | |
/** | |
* The decorated plugin discovery. | |
* | |
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface | |
*/ | |
protected $decorated; | |
/** | |
* Creates a new instance. | |
* | |
* @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated | |
* The parent object implementing DiscoveryInterface that is being | |
* decorated. | |
*/ | |
public function __construct(DiscoveryInterface $decorated) { | |
$this->decorated = $decorated; | |
} | |
/** | |
* {@inheritdoc} | |
* | |
* @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException | |
* Thrown if the 'deriver' class specified in the plugin definition | |
* does not implement \Drupal\Component\Plugin\Derivative\DeriverInterface. | |
*/ | |
public function getDefinition($plugin_id, $exception_on_invalid = TRUE) { | |
// This check is only for derivative plugins that have explicitly provided | |
// an ID. This is not common, and can be expected to fail. Therefore, opt | |
// out of the thrown exception, which will be handled when checking the | |
// $base_plugin_id. | |
$plugin_definition = $this->decorated->getDefinition($plugin_id, FALSE); | |
list($base_plugin_id, $derivative_id) = $this->decodePluginId($plugin_id); | |
$base_plugin_definition = $this->decorated->getDefinition($base_plugin_id, $exception_on_invalid); | |
if ($base_plugin_definition) { | |
$deriver = $this->getDeriver($base_plugin_id, $base_plugin_definition); | |
if ($deriver) { | |
$derivative_plugin_definition = $deriver->getDerivativeDefinition($derivative_id, $base_plugin_definition); | |
// If a plugin defined itself as a derivative, merge in possible | |
// defaults from the derivative. | |
if ($derivative_id && isset($plugin_definition)) { | |
$plugin_definition = $this->mergeDerivativeDefinition($plugin_definition, $derivative_plugin_definition); | |
} | |
else { | |
$plugin_definition = $derivative_plugin_definition; | |
} | |
} | |
} | |
return $plugin_definition; | |
} | |
/** | |
* {@inheritdoc} | |
* | |
* @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException | |
* Thrown if the 'deriver' class specified in the plugin definition | |
* does not implement \Drupal\Component\Plugin\Derivative\DeriverInterface. | |
*/ | |
public function getDefinitions() { | |
$plugin_definitions = $this->decorated->getDefinitions(); | |
return $this->getDerivatives($plugin_definitions); | |
} | |
/** | |
* Adds derivatives to a list of plugin definitions. | |
* | |
* This should be called by the class extending this in | |
* DiscoveryInterface::getDefinitions(). | |
*/ | |
protected function getDerivatives(array $base_plugin_definitions) { | |
$plugin_definitions = array(); | |
foreach ($base_plugin_definitions as $base_plugin_id => $plugin_definition) { | |
$deriver = $this->getDeriver($base_plugin_id, $plugin_definition); | |
if ($deriver) { | |
$derivative_definitions = $deriver->getDerivativeDefinitions($plugin_definition); | |
foreach ($derivative_definitions as $derivative_id => $derivative_definition) { | |
$plugin_id = $this->encodePluginId($base_plugin_id, $derivative_id); | |
// Use this definition as defaults if a plugin already defined | |
// itself as this derivative. | |
if ($derivative_id && isset($base_plugin_definitions[$plugin_id])) { | |
$derivative_definition = $this->mergeDerivativeDefinition($base_plugin_definitions[$plugin_id], $derivative_definition); | |
} | |
$plugin_definitions[$plugin_id] = $derivative_definition; | |
} | |
} | |
// If a plugin already defined itself as a derivative it might already | |
// be merged into the definitions. | |
elseif (!isset($plugin_definitions[$base_plugin_id])) { | |
$plugin_definitions[$base_plugin_id] = $plugin_definition; | |
} | |
} | |
return $plugin_definitions; | |
} | |
/** | |
* Decodes derivative id and plugin id from a string. | |
* | |
* @param string $plugin_id | |
* Plugin identifier that may point to a derivative plugin. | |
* | |
* @return array | |
* An array with the base plugin id as the first index and the derivative id | |
* as the second. If there is no derivative id it will be null. | |
*/ | |
protected function decodePluginId($plugin_id) { | |
// Try and split the passed plugin definition into a plugin and a | |
// derivative id. We don't need to check for !== FALSE because a leading | |
// colon would break the derivative system and doesn't makes sense. | |
if (strpos($plugin_id, ':')) { | |
return explode(':', $plugin_id, 2); | |
} | |
return array($plugin_id, NULL); | |
} | |
/** | |
* Encodes plugin and derivative id's into a string. | |
* | |
* @param string $base_plugin_id | |
* The base plugin identifier. | |
* @param string $derivative_id | |
* The derivative identifier. | |
* | |
* @return string | |
* A uniquely encoded combination of the $base_plugin_id and $derivative_id. | |
*/ | |
protected function encodePluginId($base_plugin_id, $derivative_id) { | |
if ($derivative_id) { | |
return "$base_plugin_id:$derivative_id"; | |
} | |
// By returning the unmerged plugin_id, we are able to support derivative | |
// plugins that support fetching the base definitions. | |
return $base_plugin_id; | |
} | |
/** | |
* Gets a deriver for a base plugin. | |
* | |
* @param string $base_plugin_id | |
* The base plugin id of the plugin. | |
* @param mixed $base_definition | |
* The base plugin definition to build derivatives. | |
* | |
* @return \Drupal\Component\Plugin\Derivative\DeriverInterface|null | |
* A DerivativeInterface or NULL if none exists for the plugin. | |
* | |
* @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException | |
* Thrown if the 'deriver' class specified in the plugin definition | |
* does not implement \Drupal\Component\Plugin\Derivative\DeriverInterface. | |
*/ | |
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) { | |
$this->derivers[$base_plugin_id] = new $class($base_plugin_id); | |
} | |
} | |
return $this->derivers[$base_plugin_id] ?: NULL; | |
} | |
/** | |
* Gets the deriver class name from the base plugin definition. | |
* | |
* @param array $base_definition | |
* The base plugin definition to build derivatives. | |
* | |
* @return string|null | |
* The name of a class implementing | |
* \Drupal\Component\Plugin\Derivative\DeriverInterface. | |
* | |
* @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException | |
* Thrown if the 'deriver' class specified in the plugin definition | |
* does not implement | |
* \Drupal\Component\Plugin\Derivative\DerivativeInterface. | |
*/ | |
protected function getDeriverClass($base_definition) { | |
$class = NULL; | |
if ((is_array($base_definition) || ($base_definition = (array) $base_definition)) && (isset($base_definition['deriver']) && $class = $base_definition['deriver'])) { | |
if (!class_exists($class)) { | |
throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" does not exist.', $base_definition['id'], $class)); | |
} | |
if (!is_subclass_of($class, '\Drupal\Component\Plugin\Derivative\DeriverInterface')) { | |
throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" must implement \Drupal\Component\Plugin\Derivative\DeriverInterface.', $base_definition['id'], $class)); | |
} | |
} | |
return $class; | |
} | |
/** | |
* Merges a base and derivative definition, taking into account empty values. | |
* | |
* @param array $base_plugin_definition | |
* The base plugin definition. | |
* @param array $derivative_definition | |
* The derivative plugin definition. | |
* | |
* @return array | |
* The merged definition. | |
*/ | |
protected function mergeDerivativeDefinition($base_plugin_definition, $derivative_definition) { | |
// Use this definition as defaults if a plugin already defined itself as | |
// this derivative, but filter out empty values first. | |
$filtered_base = array_filter($base_plugin_definition); | |
$derivative_definition = $filtered_base + ($derivative_definition ?: array()); | |
// Add back any empty keys that the derivative didn't have. | |
return $derivative_definition + $base_plugin_definition; | |
} | |
/** | |
* Passes through all unknown calls onto the decorated object. | |
*/ | |
public function __call($method, $args) { | |
return call_user_func_array(array($this->decorated, $method), $args); | |
} | |
} |