annotate core/lib/Drupal/Core/Plugin/DefaultPluginManager.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Plugin;
Chris@0 4
Chris@0 5 use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
Chris@0 6 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
Chris@0 7 use Drupal\Core\Cache\CacheableDependencyInterface;
Chris@0 8 use Drupal\Core\Cache\CacheBackendInterface;
Chris@0 9 use Drupal\Core\Cache\UseCacheBackendTrait;
Chris@0 10 use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
Chris@0 11 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
Chris@0 12 use Drupal\Component\Plugin\PluginManagerBase;
Chris@0 13 use Drupal\Component\Plugin\PluginManagerInterface;
Chris@0 14 use Drupal\Component\Utility\NestedArray;
Chris@0 15 use Drupal\Core\Cache\Cache;
Chris@0 16 use Drupal\Core\Extension\ModuleHandlerInterface;
Chris@0 17 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
Chris@0 18 use Drupal\Core\Plugin\Factory\ContainerFactory;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * Base class for plugin managers.
Chris@0 22 *
Chris@0 23 * @ingroup plugin_api
Chris@0 24 */
Chris@0 25 class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface, CacheableDependencyInterface {
Chris@0 26
Chris@0 27 use DiscoveryCachedTrait;
Chris@0 28 use UseCacheBackendTrait;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * The cache key.
Chris@0 32 *
Chris@0 33 * @var string
Chris@0 34 */
Chris@0 35 protected $cacheKey;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * An array of cache tags to use for the cached definitions.
Chris@0 39 *
Chris@0 40 * @var array
Chris@0 41 */
Chris@0 42 protected $cacheTags = [];
Chris@0 43
Chris@0 44 /**
Chris@0 45 * Name of the alter hook if one should be invoked.
Chris@0 46 *
Chris@0 47 * @var string
Chris@0 48 */
Chris@0 49 protected $alterHook;
Chris@0 50
Chris@0 51 /**
Chris@0 52 * The subdirectory within a namespace to look for plugins, or FALSE if the
Chris@0 53 * plugins are in the top level of the namespace.
Chris@0 54 *
Chris@0 55 * @var string|bool
Chris@0 56 */
Chris@0 57 protected $subdir;
Chris@0 58
Chris@0 59 /**
Chris@0 60 * The module handler to invoke the alter hook.
Chris@0 61 *
Chris@0 62 * @var \Drupal\Core\Extension\ModuleHandlerInterface
Chris@0 63 */
Chris@0 64 protected $moduleHandler;
Chris@0 65
Chris@0 66 /**
Chris@0 67 * A set of defaults to be referenced by $this->processDefinition() if
Chris@0 68 * additional processing of plugins is necessary or helpful for development
Chris@0 69 * purposes.
Chris@0 70 *
Chris@0 71 * @var array
Chris@0 72 */
Chris@0 73 protected $defaults = [];
Chris@0 74
Chris@0 75 /**
Chris@0 76 * The name of the annotation that contains the plugin definition.
Chris@0 77 *
Chris@0 78 * @var string
Chris@0 79 */
Chris@0 80 protected $pluginDefinitionAnnotationName;
Chris@0 81
Chris@0 82 /**
Chris@0 83 * The interface each plugin should implement.
Chris@0 84 *
Chris@0 85 * @var string|null
Chris@0 86 */
Chris@0 87 protected $pluginInterface;
Chris@0 88
Chris@0 89 /**
Chris@0 90 * An object that implements \Traversable which contains the root paths
Chris@0 91 * keyed by the corresponding namespace to look for plugin implementations.
Chris@0 92 *
Chris@0 93 * @var \Traversable
Chris@0 94 */
Chris@0 95 protected $namespaces;
Chris@0 96
Chris@0 97 /**
Chris@0 98 * Additional namespaces the annotation discovery mechanism should scan for
Chris@0 99 * annotation definitions.
Chris@0 100 *
Chris@0 101 * @var string[]
Chris@0 102 */
Chris@0 103 protected $additionalAnnotationNamespaces = [];
Chris@0 104
Chris@0 105 /**
Chris@0 106 * Creates the discovery object.
Chris@0 107 *
Chris@0 108 * @param string|bool $subdir
Chris@0 109 * The plugin's subdirectory, for example Plugin/views/filter.
Chris@0 110 * @param \Traversable $namespaces
Chris@0 111 * An object that implements \Traversable which contains the root paths
Chris@0 112 * keyed by the corresponding namespace to look for plugin implementations.
Chris@0 113 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
Chris@0 114 * The module handler.
Chris@0 115 * @param string|null $plugin_interface
Chris@0 116 * (optional) The interface each plugin should implement.
Chris@0 117 * @param string $plugin_definition_annotation_name
Chris@0 118 * (optional) The name of the annotation that contains the plugin definition.
Chris@0 119 * Defaults to 'Drupal\Component\Annotation\Plugin'.
Chris@0 120 * @param string[] $additional_annotation_namespaces
Chris@0 121 * (optional) Additional namespaces to scan for annotation definitions.
Chris@0 122 */
Chris@0 123 public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInterface $module_handler, $plugin_interface = NULL, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', array $additional_annotation_namespaces = []) {
Chris@0 124 $this->subdir = $subdir;
Chris@0 125 $this->namespaces = $namespaces;
Chris@0 126 $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
Chris@0 127 $this->pluginInterface = $plugin_interface;
Chris@0 128 $this->moduleHandler = $module_handler;
Chris@0 129 $this->additionalAnnotationNamespaces = $additional_annotation_namespaces;
Chris@0 130 }
Chris@0 131
Chris@0 132 /**
Chris@0 133 * Initialize the cache backend.
Chris@0 134 *
Chris@0 135 * Plugin definitions are cached using the provided cache backend.
Chris@0 136 *
Chris@0 137 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
Chris@0 138 * Cache backend instance to use.
Chris@0 139 * @param string $cache_key
Chris@0 140 * Cache key prefix to use.
Chris@0 141 * @param array $cache_tags
Chris@0 142 * (optional) When providing a list of cache tags, the cached plugin
Chris@0 143 * definitions are tagged with the provided cache tags. These cache tags can
Chris@0 144 * then be used to clear the corresponding cached plugin definitions. Note
Chris@0 145 * that this should be used with care! For clearing all cached plugin
Chris@0 146 * definitions of a plugin manager, call that plugin manager's
Chris@0 147 * clearCachedDefinitions() method. Only use cache tags when cached plugin
Chris@0 148 * definitions should be cleared along with other, related cache entries.
Chris@0 149 */
Chris@0 150 public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = []) {
Chris@0 151 assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($cache_tags)', 'Cache Tags must be strings.');
Chris@0 152 $this->cacheBackend = $cache_backend;
Chris@0 153 $this->cacheKey = $cache_key;
Chris@0 154 $this->cacheTags = $cache_tags;
Chris@0 155 }
Chris@0 156
Chris@0 157 /**
Chris@0 158 * Initializes the alter hook.
Chris@0 159 *
Chris@0 160 * @param string $alter_hook
Chris@0 161 * Name of the alter hook; for example, to invoke
Chris@0 162 * hook_mymodule_data_alter() pass in "mymodule_data".
Chris@0 163 */
Chris@0 164 protected function alterInfo($alter_hook) {
Chris@0 165 $this->alterHook = $alter_hook;
Chris@0 166 }
Chris@0 167
Chris@0 168 /**
Chris@0 169 * {@inheritdoc}
Chris@0 170 */
Chris@0 171 public function getDefinitions() {
Chris@0 172 $definitions = $this->getCachedDefinitions();
Chris@0 173 if (!isset($definitions)) {
Chris@0 174 $definitions = $this->findDefinitions();
Chris@0 175 $this->setCachedDefinitions($definitions);
Chris@0 176 }
Chris@0 177 return $definitions;
Chris@0 178 }
Chris@0 179
Chris@0 180 /**
Chris@0 181 * {@inheritdoc}
Chris@0 182 */
Chris@0 183 public function clearCachedDefinitions() {
Chris@0 184 if ($this->cacheBackend) {
Chris@0 185 if ($this->cacheTags) {
Chris@0 186 // Use the cache tags to clear the cache.
Chris@0 187 Cache::invalidateTags($this->cacheTags);
Chris@0 188 }
Chris@0 189 else {
Chris@0 190 $this->cacheBackend->delete($this->cacheKey);
Chris@0 191 }
Chris@0 192 }
Chris@0 193 $this->definitions = NULL;
Chris@0 194 }
Chris@0 195
Chris@0 196 /**
Chris@0 197 * Returns the cached plugin definitions of the decorated discovery class.
Chris@0 198 *
Chris@0 199 * @return array|null
Chris@0 200 * On success this will return an array of plugin definitions. On failure
Chris@0 201 * this should return NULL, indicating to other methods that this has not
Chris@0 202 * yet been defined. Success with no values should return as an empty array
Chris@0 203 * and would actually be returned by the getDefinitions() method.
Chris@0 204 */
Chris@0 205 protected function getCachedDefinitions() {
Chris@0 206 if (!isset($this->definitions) && $cache = $this->cacheGet($this->cacheKey)) {
Chris@0 207 $this->definitions = $cache->data;
Chris@0 208 }
Chris@0 209 return $this->definitions;
Chris@0 210 }
Chris@0 211
Chris@0 212 /**
Chris@0 213 * Sets a cache of plugin definitions for the decorated discovery class.
Chris@0 214 *
Chris@0 215 * @param array $definitions
Chris@0 216 * List of definitions to store in cache.
Chris@0 217 */
Chris@0 218 protected function setCachedDefinitions($definitions) {
Chris@0 219 $this->cacheSet($this->cacheKey, $definitions, Cache::PERMANENT, $this->cacheTags);
Chris@0 220 $this->definitions = $definitions;
Chris@0 221 }
Chris@0 222
Chris@0 223 /**
Chris@0 224 * {@inheritdoc}
Chris@0 225 */
Chris@0 226 public function useCaches($use_caches = FALSE) {
Chris@0 227 $this->useCaches = $use_caches;
Chris@0 228 if (!$use_caches) {
Chris@0 229 $this->definitions = NULL;
Chris@0 230 }
Chris@0 231 }
Chris@0 232
Chris@0 233 /**
Chris@0 234 * Performs extra processing on plugin definitions.
Chris@0 235 *
Chris@0 236 * By default we add defaults for the type to the definition. If a type has
Chris@0 237 * additional processing logic they can do that by replacing or extending the
Chris@0 238 * method.
Chris@0 239 */
Chris@0 240 public function processDefinition(&$definition, $plugin_id) {
Chris@0 241 // Only array-based definitions can have defaults merged in.
Chris@0 242 if (is_array($definition) && !empty($this->defaults) && is_array($this->defaults)) {
Chris@0 243 $definition = NestedArray::mergeDeep($this->defaults, $definition);
Chris@0 244 }
Chris@0 245
Chris@0 246 // Keep class definitions standard with no leading slash.
Chris@0 247 if ($definition instanceof PluginDefinitionInterface) {
Chris@0 248 $definition->setClass(ltrim($definition->getClass(), '\\'));
Chris@0 249 }
Chris@0 250 elseif (is_array($definition) && isset($definition['class'])) {
Chris@0 251 $definition['class'] = ltrim($definition['class'], '\\');
Chris@0 252 }
Chris@0 253 }
Chris@0 254
Chris@0 255 /**
Chris@0 256 * {@inheritdoc}
Chris@0 257 */
Chris@0 258 protected function getDiscovery() {
Chris@0 259 if (!$this->discovery) {
Chris@0 260 $discovery = new AnnotatedClassDiscovery($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces);
Chris@0 261 $this->discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
Chris@0 262 }
Chris@0 263 return $this->discovery;
Chris@0 264 }
Chris@0 265
Chris@0 266 /**
Chris@0 267 * {@inheritdoc}
Chris@0 268 */
Chris@0 269 protected function getFactory() {
Chris@0 270 if (!$this->factory) {
Chris@0 271 $this->factory = new ContainerFactory($this, $this->pluginInterface);
Chris@0 272 }
Chris@0 273 return $this->factory;
Chris@0 274 }
Chris@0 275
Chris@0 276 /**
Chris@0 277 * Finds plugin definitions.
Chris@0 278 *
Chris@0 279 * @return array
Chris@0 280 * List of definitions to store in cache.
Chris@0 281 */
Chris@0 282 protected function findDefinitions() {
Chris@0 283 $definitions = $this->getDiscovery()->getDefinitions();
Chris@0 284 foreach ($definitions as $plugin_id => &$definition) {
Chris@0 285 $this->processDefinition($definition, $plugin_id);
Chris@0 286 }
Chris@0 287 $this->alterDefinitions($definitions);
Chris@0 288 // If this plugin was provided by a module that does not exist, remove the
Chris@0 289 // plugin definition.
Chris@0 290 foreach ($definitions as $plugin_id => $plugin_definition) {
Chris@0 291 $provider = $this->extractProviderFromDefinition($plugin_definition);
Chris@0 292 if ($provider && !in_array($provider, ['core', 'component']) && !$this->providerExists($provider)) {
Chris@0 293 unset($definitions[$plugin_id]);
Chris@0 294 }
Chris@0 295 }
Chris@0 296 return $definitions;
Chris@0 297 }
Chris@0 298
Chris@0 299 /**
Chris@0 300 * Extracts the provider from a plugin definition.
Chris@0 301 *
Chris@0 302 * @param mixed $plugin_definition
Chris@0 303 * The plugin definition. Usually either an array or an instance of
Chris@0 304 * \Drupal\Component\Plugin\Definition\PluginDefinitionInterface
Chris@0 305 *
Chris@0 306 * @return string|null
Chris@0 307 * The provider string, if it exists. NULL otherwise.
Chris@0 308 */
Chris@0 309 protected function extractProviderFromDefinition($plugin_definition) {
Chris@0 310 if ($plugin_definition instanceof PluginDefinitionInterface) {
Chris@0 311 return $plugin_definition->getProvider();
Chris@0 312 }
Chris@0 313
Chris@0 314 // Attempt to convert the plugin definition to an array.
Chris@0 315 if (is_object($plugin_definition)) {
Chris@0 316 $plugin_definition = (array) $plugin_definition;
Chris@0 317 }
Chris@0 318
Chris@0 319 if (isset($plugin_definition['provider'])) {
Chris@0 320 return $plugin_definition['provider'];
Chris@0 321 }
Chris@0 322 }
Chris@0 323
Chris@0 324 /**
Chris@0 325 * Invokes the hook to alter the definitions if the alter hook is set.
Chris@0 326 *
Chris@0 327 * @param $definitions
Chris@0 328 * The discovered plugin definitions.
Chris@0 329 */
Chris@0 330 protected function alterDefinitions(&$definitions) {
Chris@0 331 if ($this->alterHook) {
Chris@0 332 $this->moduleHandler->alter($this->alterHook, $definitions);
Chris@0 333 }
Chris@0 334 }
Chris@0 335
Chris@0 336 /**
Chris@0 337 * Determines if the provider of a definition exists.
Chris@0 338 *
Chris@0 339 * @return bool
Chris@0 340 * TRUE if provider exists, FALSE otherwise.
Chris@0 341 */
Chris@0 342 protected function providerExists($provider) {
Chris@0 343 return $this->moduleHandler->moduleExists($provider);
Chris@0 344 }
Chris@0 345
Chris@0 346 /**
Chris@0 347 * {@inheritdoc}
Chris@0 348 */
Chris@0 349 public function getCacheContexts() {
Chris@0 350 return [];
Chris@0 351 }
Chris@0 352
Chris@0 353 /**
Chris@0 354 * {@inheritdoc}
Chris@0 355 */
Chris@0 356 public function getCacheTags() {
Chris@0 357 return $this->cacheTags;
Chris@0 358 }
Chris@0 359
Chris@0 360 /**
Chris@0 361 * {@inheritdoc}
Chris@0 362 */
Chris@0 363 public function getCacheMaxAge() {
Chris@0 364 return Cache::PERMANENT;
Chris@0 365 }
Chris@0 366
Chris@0 367 }