toolshed-8.x-1.x-dev/src/Plugin/YamlPluginManager.php
src/Plugin/YamlPluginManager.php
<?php
namespace Drupal\toolshed\Plugin;
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
use Drupal\Component\Plugin\PluginManagerBase;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\UseCacheBackendTrait;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;
/**
* Plugin manager for plugins that are defined from YAML files.
*/
class YamlPluginManager extends PluginManagerBase implements CachedDiscoveryInterface {
use DiscoveryCachedTrait;
use UseCacheBackendTrait;
/**
* The fully namespace interface name that plugin instances should implement.
*
* @var class-string
*/
protected string $pluginInterface;
/**
* The loaded plugin definitions if they've been loaded.
*
* @var array|null
*/
protected $definitions;
/**
* The name of the plugin to use as in the discovery.
*
* The string should contain only characters [a-z][0-9], periods or
* underscores and will be used in the discovery handler to find definition
* files with the naming convention "<module>.<pluginName>.yml".
*
* @var string
*/
protected string $pluginName;
/**
* The fully scoped class name, of the default plugin implementation class.
*
* @var class-string|null
*/
protected ?string $defaultPluginClass = NULL;
/**
* Module extension handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|null
*/
protected ?ModuleHandlerInterface $moduleHandler = NULL;
/**
* Theme extension handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface|null
*/
protected ?ThemeHandlerInterface $themeHandler = NULL;
/**
* The key of the cache data, holding the discovered plugin definitions.
*
* @var string|null
*/
protected ?string $cacheKey = NULL;
/**
* Create a new instance of the plugin manager using YAML file discovery.
*
* @param string $scope_name
* The name of the provider of the plugin manager. Typically this is the
* module name, where the manager is defined.
* @param class-string $plugin_interface
* Interface which the plugin instances need to implement. Reinforces
* base functionality that utilizers of the plugin can assume.
* @param class-string|null $default_class
* The default plugin class to use when the plugin definition does not
* include information about the class type.
* @param \Drupal\Core\Extension\ModuleHandlerInterface|\Drupal\Core\Extension\ThemeHandlerInterface $extension_handler
* Service which manages active modules.
* @param \Drupal\Core\Cache\CacheBackendInterface|null $cache_backend
* The cache to store the plugin discovery information.
* @param string|null $cache_key
* The key to use when caching the discovery of plugin definitions.
*/
public function __construct(string $scope_name, string $plugin_interface, ?string $default_class, ModuleHandlerInterface|ThemeHandlerInterface $extension_handler, ?CacheBackendInterface $cache_backend = NULL, ?string $cache_key = NULL) {
$this->pluginName = $scope_name;
$this->pluginInterface = $plugin_interface;
$this->defaultPluginClass = $default_class;
if ($extension_handler instanceof ModuleHandlerInterface) {
$this->moduleHandler = $extension_handler;
}
else {
$this->themeHandler = $extension_handler;
}
$this->setCacheBackend($scope_name, $cache_backend, $cache_key);
}
/**
* Set the cache backend service for this plugin manager instance.
*
* @param string $scope
* The plugin name or scope to use for naming of caches.
* @param \Drupal\Core\Cache\CacheBackendInterface|null $cacheBackend
* The cache backend to store the plugin definitions to.
* @param string|null $cacheKey
* A cache key that can be used for invalidations.
*/
protected function setCacheBackend(string $scope, ?CacheBackendInterface $cacheBackend = NULL, ?string $cacheKey = NULL): void {
if ($cacheBackend) {
$this->cacheBackend = $cacheBackend;
$this->cacheKey = !empty($cacheKey) ? $cacheKey : strtr($scope, '.', '_');
}
}
/**
* {@inheritdoc}
*/
protected function providerExists($provider): bool {
// Check the if the provider extension exists.
return ($this->moduleHandler && $this->moduleHandler->moduleExists($provider))
|| ($this->themeHandler && $this->themeHandler->themeExists($provider));
}
/**
* {@inheritdoc}
*/
public function getDiscovery() {
if (!$this->discovery) {
$directories = [];
if ($this->moduleHandler) {
$directories = $this->moduleHandler->getModuleDirectories();
}
if ($this->themeHandler) {
$directories = array_merge($directories, $this->themeHandler->getThemeDirectories());
}
$discovery = new YamlDiscovery($this->pluginName, $directories);
$discovery->addTranslatableProperty('label', 'label_context');
$discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
$this->discovery = $discovery;
}
return $this->discovery;
}
/**
* {@inheritdoc}
*/
public function getFactory() {
if (!$this->factory) {
$this->factory = new ContainerFactory($this, $this->pluginInterface);
}
return $this->factory;
}
/**
* {@inheritdoc}
*/
public function useCaches($use_caches = FALSE) {
$this->useCaches = $use_caches;
if (!$this->useCaches) {
$this->definitions = NULL;
}
}
/**
* {@inheritdoc}
*/
public function clearCachedDefinitions() {
if ($this->cacheBackend) {
$this->cacheBackend->delete($this->cacheKey);
}
$this->definitions = NULL;
}
/**
* Use the plugin manager discovery to find and process definitions.
*
* @return array
* An array of plugins discovered and processed by they discovery instance.
*/
protected function findDefinitions() {
$definitions = $this->getDiscovery()
->getDefinitions();
foreach ($definitions as $plugin_id => &$definition) {
$this->processDefinition($definition, $plugin_id);
}
return $definitions;
}
/**
* Process individual raw definitions from the discovery handler.
*
* Apply defaults and alter the raw definitions as needed to ensure that the
* definitions are complete and allow for common defaults.
*
* @param mixed $definition
* Refereance to an array of plugin definition information that can be
* altered with defaults or other scope changes.
* @param string $plugin_id
* The ID of the plugin being processed.
*/
protected function processDefinition(&$definition, $plugin_id) {
if (is_array($definition) && empty($definition['class'])) {
$definition['class'] = $this->defaultPluginClass;
}
}
/**
* {@inheritdoc}
*/
public function getDefinitions() {
if (!isset($this->definitions)) {
if ($cache = $this->cacheGet($this->cacheKey)) {
$this->definitions = $cache->data;
}
else {
$this->definitions = $this->findDefinitions();
$this->cacheSet($this->cacheKey, $this->definitions);
}
}
return $this->definitions;
}
}
