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