/sites/all/modules/service_container/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php

https://gitlab.com/leoplanxxi/dr7-web-buap-2016 · PHP · 248 lines · 95 code · 22 blank · 131 comment · 18 complexity · 2c4ea66053ac9ebc9ac9dd6807a81c36 MD5 · raw file

  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator.
  5. */
  6. namespace Drupal\Component\Plugin\Discovery;
  7. use Drupal\Component\Plugin\Exception\InvalidDeriverException;
  8. /**
  9. * Base class providing the tools for a plugin discovery to be derivative aware.
  10. *
  11. * Provides a decorator that allows the use of plugin derivatives for normal
  12. * implementations DiscoveryInterface.
  13. */
  14. class DerivativeDiscoveryDecorator implements DiscoveryInterface {
  15. use DiscoveryTrait;
  16. /**
  17. * Plugin derivers.
  18. *
  19. * @var \Drupal\Component\Plugin\Derivative\DeriverInterface[]
  20. * Keys are base plugin IDs.
  21. */
  22. protected $derivers = array();
  23. /**
  24. * The decorated plugin discovery.
  25. *
  26. * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
  27. */
  28. protected $decorated;
  29. /**
  30. * Creates a new instance.
  31. *
  32. * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
  33. * The parent object implementing DiscoveryInterface that is being
  34. * decorated.
  35. */
  36. public function __construct(DiscoveryInterface $decorated) {
  37. $this->decorated = $decorated;
  38. }
  39. /**
  40. * {@inheritdoc}
  41. *
  42. * @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException
  43. * Thrown if the 'deriver' class specified in the plugin definition
  44. * does not implement \Drupal\Component\Plugin\Derivative\DeriverInterface.
  45. */
  46. public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
  47. // This check is only for derivative plugins that have explicitly provided
  48. // an ID. This is not common, and can be expected to fail. Therefore, opt
  49. // out of the thrown exception, which will be handled when checking the
  50. // $base_plugin_id.
  51. $plugin_definition = $this->decorated->getDefinition($plugin_id, FALSE);
  52. list($base_plugin_id, $derivative_id) = $this->decodePluginId($plugin_id);
  53. $base_plugin_definition = $this->decorated->getDefinition($base_plugin_id, $exception_on_invalid);
  54. if ($base_plugin_definition) {
  55. $deriver = $this->getDeriver($base_plugin_id, $base_plugin_definition);
  56. if ($deriver) {
  57. $derivative_plugin_definition = $deriver->getDerivativeDefinition($derivative_id, $base_plugin_definition);
  58. // If a plugin defined itself as a derivative, merge in possible
  59. // defaults from the derivative.
  60. if ($derivative_id && isset($plugin_definition)) {
  61. $plugin_definition = $this->mergeDerivativeDefinition($plugin_definition, $derivative_plugin_definition);
  62. }
  63. else {
  64. $plugin_definition = $derivative_plugin_definition;
  65. }
  66. }
  67. }
  68. return $plugin_definition;
  69. }
  70. /**
  71. * {@inheritdoc}
  72. *
  73. * @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException
  74. * Thrown if the 'deriver' class specified in the plugin definition
  75. * does not implement \Drupal\Component\Plugin\Derivative\DeriverInterface.
  76. */
  77. public function getDefinitions() {
  78. $plugin_definitions = $this->decorated->getDefinitions();
  79. return $this->getDerivatives($plugin_definitions);
  80. }
  81. /**
  82. * Adds derivatives to a list of plugin definitions.
  83. *
  84. * This should be called by the class extending this in
  85. * DiscoveryInterface::getDefinitions().
  86. */
  87. protected function getDerivatives(array $base_plugin_definitions) {
  88. $plugin_definitions = array();
  89. foreach ($base_plugin_definitions as $base_plugin_id => $plugin_definition) {
  90. $deriver = $this->getDeriver($base_plugin_id, $plugin_definition);
  91. if ($deriver) {
  92. $derivative_definitions = $deriver->getDerivativeDefinitions($plugin_definition);
  93. foreach ($derivative_definitions as $derivative_id => $derivative_definition) {
  94. $plugin_id = $this->encodePluginId($base_plugin_id, $derivative_id);
  95. // Use this definition as defaults if a plugin already defined
  96. // itself as this derivative.
  97. if ($derivative_id && isset($base_plugin_definitions[$plugin_id])) {
  98. $derivative_definition = $this->mergeDerivativeDefinition($base_plugin_definitions[$plugin_id], $derivative_definition);
  99. }
  100. $plugin_definitions[$plugin_id] = $derivative_definition;
  101. }
  102. }
  103. // If a plugin already defined itself as a derivative it might already
  104. // be merged into the definitions.
  105. elseif (!isset($plugin_definitions[$base_plugin_id])) {
  106. $plugin_definitions[$base_plugin_id] = $plugin_definition;
  107. }
  108. }
  109. return $plugin_definitions;
  110. }
  111. /**
  112. * Decodes derivative id and plugin id from a string.
  113. *
  114. * @param string $plugin_id
  115. * Plugin identifier that may point to a derivative plugin.
  116. *
  117. * @return array
  118. * An array with the base plugin id as the first index and the derivative id
  119. * as the second. If there is no derivative id it will be null.
  120. */
  121. protected function decodePluginId($plugin_id) {
  122. // Try and split the passed plugin definition into a plugin and a
  123. // derivative id. We don't need to check for !== FALSE because a leading
  124. // colon would break the derivative system and doesn't makes sense.
  125. if (strpos($plugin_id, ':')) {
  126. return explode(':', $plugin_id, 2);
  127. }
  128. return array($plugin_id, NULL);
  129. }
  130. /**
  131. * Encodes plugin and derivative id's into a string.
  132. *
  133. * @param string $base_plugin_id
  134. * The base plugin identifier.
  135. * @param string $derivative_id
  136. * The derivative identifier.
  137. *
  138. * @return string
  139. * A uniquely encoded combination of the $base_plugin_id and $derivative_id.
  140. */
  141. protected function encodePluginId($base_plugin_id, $derivative_id) {
  142. if ($derivative_id) {
  143. return "$base_plugin_id:$derivative_id";
  144. }
  145. // By returning the unmerged plugin_id, we are able to support derivative
  146. // plugins that support fetching the base definitions.
  147. return $base_plugin_id;
  148. }
  149. /**
  150. * Gets a deriver for a base plugin.
  151. *
  152. * @param string $base_plugin_id
  153. * The base plugin id of the plugin.
  154. * @param mixed $base_definition
  155. * The base plugin definition to build derivatives.
  156. *
  157. * @return \Drupal\Component\Plugin\Derivative\DeriverInterface|null
  158. * A DerivativeInterface or NULL if none exists for the plugin.
  159. *
  160. * @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException
  161. * Thrown if the 'deriver' class specified in the plugin definition
  162. * does not implement \Drupal\Component\Plugin\Derivative\DeriverInterface.
  163. */
  164. protected function getDeriver($base_plugin_id, $base_definition) {
  165. if (!isset($this->derivers[$base_plugin_id])) {
  166. $this->derivers[$base_plugin_id] = FALSE;
  167. $class = $this->getDeriverClass($base_definition);
  168. if ($class) {
  169. $this->derivers[$base_plugin_id] = new $class($base_plugin_id);
  170. }
  171. }
  172. return $this->derivers[$base_plugin_id] ?: NULL;
  173. }
  174. /**
  175. * Gets the deriver class name from the base plugin definition.
  176. *
  177. * @param array $base_definition
  178. * The base plugin definition to build derivatives.
  179. *
  180. * @return string|null
  181. * The name of a class implementing
  182. * \Drupal\Component\Plugin\Derivative\DeriverInterface.
  183. *
  184. * @throws \Drupal\Component\Plugin\Exception\InvalidDeriverException
  185. * Thrown if the 'deriver' class specified in the plugin definition
  186. * does not implement
  187. * \Drupal\Component\Plugin\Derivative\DerivativeInterface.
  188. */
  189. protected function getDeriverClass($base_definition) {
  190. $class = NULL;
  191. if ((is_array($base_definition) || ($base_definition = (array) $base_definition)) && (isset($base_definition['deriver']) && $class = $base_definition['deriver'])) {
  192. if (!class_exists($class)) {
  193. throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" does not exist.', $base_definition['id'], $class));
  194. }
  195. if (!is_subclass_of($class, '\Drupal\Component\Plugin\Derivative\DeriverInterface')) {
  196. throw new InvalidDeriverException(sprintf('Plugin (%s) deriver "%s" must implement \Drupal\Component\Plugin\Derivative\DeriverInterface.', $base_definition['id'], $class));
  197. }
  198. }
  199. return $class;
  200. }
  201. /**
  202. * Merges a base and derivative definition, taking into account empty values.
  203. *
  204. * @param array $base_plugin_definition
  205. * The base plugin definition.
  206. * @param array $derivative_definition
  207. * The derivative plugin definition.
  208. *
  209. * @return array
  210. * The merged definition.
  211. */
  212. protected function mergeDerivativeDefinition($base_plugin_definition, $derivative_definition) {
  213. // Use this definition as defaults if a plugin already defined itself as
  214. // this derivative, but filter out empty values first.
  215. $filtered_base = array_filter($base_plugin_definition);
  216. $derivative_definition = $filtered_base + ($derivative_definition ?: array());
  217. // Add back any empty keys that the derivative didn't have.
  218. return $derivative_definition + $base_plugin_definition;
  219. }
  220. /**
  221. * Passes through all unknown calls onto the decorated object.
  222. */
  223. public function __call($method, $args) {
  224. return call_user_func_array(array($this->decorated, $method), $args);
  225. }
  226. }