DefaultPluginManager.php 8.26 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
<?php

/**
 * @file
 * Contains \Drupal\Core\Plugin\DefaultPluginManager
 */

namespace Drupal\Core\Plugin;

use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
11
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
12 13 14
use Drupal\Component\Plugin\PluginManagerBase;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Utility\NestedArray;
15
use Drupal\Core\Cache\Cache;
16 17
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
18
use Drupal\Core\Language\LanguageManagerInterface;
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
use Drupal\Core\Language\Language;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;

/**
 * Base class for plugin managers.
 */
class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface {

  /**
   * Cached definitions array.
   *
   * @var array
   */
  protected $definitions;

  /**
   * Cache backend instance.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cacheBackend;

  /**
   * Provided cache key prefix.
   *
   * @var string
   */
  protected $cacheKeyPrefix;

  /**
   * Actually used cache key with the language code appended.
   *
   * @var string
   */
  protected $cacheKey;

56 57 58 59 60 61 62
  /**
   * An array of cache tags to use for the cached definitions.
   *
   * @var array
   */
  protected $cacheTags = array();

63 64 65 66 67 68 69 70
  /**
   * Name of the alter hook if one should be invoked.
   *
   * @var string
   */
  protected $alterHook;

  /**
71 72
   * The subdirectory within a namespace to look for plugins, or FALSE if the
   * plugins are in the top level of the namespace.
73
   *
74
   * @var string|bool
75 76 77 78 79 80 81 82 83 84 85 86 87
   */
  protected $subdir;

  /**
   * The module handler to invoke the alter hook.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The language manager.
   *
88
   * @var \Drupal\Core\Language\LanguageManagerInterface
89 90 91
   */
  protected $languageManager;

92 93 94 95 96 97 98 99 100
  /**
   * A set of defaults to be referenced by $this->processDefinition() if
   * additional processing of plugins is necessary or helpful for development
   * purposes.
   *
   * @var array
   */
  protected $defaults = array();

101 102 103
  /**
   * Creates the discovery object.
   *
104 105
   * @param string|bool $subdir
   *   The plugin's subdirectory, for example Plugin/views/filter.
106 107
   * @param \Traversable $namespaces
   *   An object that implements \Traversable which contains the root paths
108
   *   keyed by the corresponding namespace to look for plugin implementations.
109 110 111 112
   * @param string $plugin_definition_annotation_name
   *   (optional) The name of the annotation that contains the plugin definition.
   *   Defaults to 'Drupal\Component\Annotation\Plugin'.
   */
113
  public function __construct($subdir, \Traversable $namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
114
    $this->subdir = $subdir;
115
    $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $plugin_definition_annotation_name);
116
    $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
117 118 119 120 121 122 123 124 125 126 127
    $this->factory = new ContainerFactory($this);
  }

  /**
   * Initialize the cache backend.
   *
   * Plugin definitions are cached using the provided cache backend. The
   * interface language is added as a suffix to the cache key.
   *
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   *   Cache backend instance to use.
128
   * @param \Drupal\Core\Language\LanguageManagerInterface
129 130 131 132
   *   The language manager.
   * @param string $cache_key_prefix
   *   Cache key prefix to use, the language code will be appended
   *   automatically.
133
   * @param array $cache_tags
134 135 136 137 138 139 140
   *   (optional) When providing a list of cache tags, the cached plugin
   *   definitions are tagged with the provided cache tags. These cache tags can
   *   then be used to clear the corresponding cached plugin definitions. Note
   *   that this should be used with care! For clearing all cached plugin
   *   definitions of a plugin manager, call that plugin manager's
   *   clearCachedDefinitions() method. Only use cache tags when cached plugin
   *   definitions should be cleared along with other, related cache entries.
141
   */
142
  public function setCacheBackend(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, $cache_key_prefix, array $cache_tags = array()) {
143 144 145
    $this->languageManager = $language_manager;
    $this->cacheBackend = $cache_backend;
    $this->cacheKeyPrefix = $cache_key_prefix;
146
    $this->cacheKey = $cache_key_prefix . ':' . $language_manager->getCurrentLanguage()->id;
147
    $this->cacheTags = $cache_tags;
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
  }

  /**
   * Initializes the alter hook.
   *
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler to invoke the alter hook with.
   * @param string $alter_hook
   *   Name of the alter hook.
   */
  protected function alterInfo(ModuleHandlerInterface $module_handler, $alter_hook) {
    $this->moduleHandler = $module_handler;
    $this->alterHook = $alter_hook;
  }

  /**
   * {@inheritdoc}
   */
  public function getDefinition($plugin_id) {
    // Fetch definitions if they're not loaded yet.
    if (!isset($this->definitions)) {
      $this->getDefinitions();
    }
    // Avoid using a ternary that would create a copy of the array.
    if (isset($this->definitions[$plugin_id])) {
      return $this->definitions[$plugin_id];
    }
    return array();
  }

  /**
   * {@inheritdoc}
   */
  public function getDefinitions() {
    $definitions = $this->getCachedDefinitions();
    if (!isset($definitions)) {
      $definitions = $this->findDefinitions();
      $this->setCachedDefinitions($definitions);
    }
    return $definitions;
  }

  /**
   * {@inheritdoc}
   */
  public function clearCachedDefinitions() {
    if ($this->cacheBackend) {
195 196
      if ($this->cacheTags) {
        // Use the cache tags to clear the cache.
197
        Cache::deleteTags($this->cacheTags);
198
      }
199
      elseif ($this->languageManager) {
200
        $cache_keys = array();
201 202
        foreach ($this->languageManager->getLanguages() as $langcode => $language) {
          $cache_keys[] = $this->cacheKeyPrefix . ':' . $langcode;
203 204
        }
        $this->cacheBackend->deleteMultiple($cache_keys);
205
      }
206 207 208
      else {
        $this->cacheBackend->delete($this->cacheKey);
      }
209 210 211 212 213 214 215
    }
    $this->definitions = NULL;
  }

  /**
   * Returns the cached plugin definitions of the decorated discovery class.
   *
216
   * @return array|null
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
   *   On success this will return an array of plugin definitions. On failure
   *   this should return NULL, indicating to other methods that this has not
   *   yet been defined. Success with no values should return as an empty array
   *   and would actually be returned by the getDefinitions() method.
   */
  protected function getCachedDefinitions() {
    if (!isset($this->definitions) && $this->cacheBackend && $cache = $this->cacheBackend->get($this->cacheKey)) {
      $this->definitions = $cache->data;
    }
    return $this->definitions;
  }

  /**
   * Sets a cache of plugin definitions for the decorated discovery class.
   *
   * @param array $definitions
   *   List of definitions to store in cache.
   */
  protected function setCachedDefinitions($definitions) {
    if ($this->cacheBackend) {
237
      $this->cacheBackend->set($this->cacheKey, $definitions, Cache::PERMANENT, $this->cacheTags);
238 239 240 241
    }
    $this->definitions = $definitions;
  }

242 243 244 245 246 247 248 249 250 251 252 253 254 255

  /**
   * Performs extra processing on plugin definitions.
   *
   * By default we add defaults for the type to the definition. If a type has
   * additional processing logic they can do that by replacing or extending the
   * method.
   */
  public function processDefinition(&$definition, $plugin_id) {
    if (!empty($this->defaults) && is_array($this->defaults)) {
      $definition = NestedArray::mergeDeep($this->defaults, $definition);
    }
  }

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  /**
   * Finds plugin definitions.
   *
   * @return array
   *   List of definitions to store in cache.
   */
  protected function findDefinitions() {
    $definitions = $this->discovery->getDefinitions();
    foreach ($definitions as $plugin_id => &$definition) {
      $this->processDefinition($definition, $plugin_id);
    }
    if ($this->alterHook) {
      $this->moduleHandler->alter($this->alterHook, $definitions);
    }
    return $definitions;
  }

}