preprocessors-8.x-1.0-beta8/src/PreprocessorsPluginManager.php
src/PreprocessorsPluginManager.php
<?php
namespace Drupal\preprocessors;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Plugin\Discovery\AttributeDiscoveryWithAnnotations;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\preprocessors\Annotation\Preprocessor as PreprocessorAnnotation;
use Drupal\preprocessors\Attribute\Preprocessor as PreprocessorAttribute;
/**
* Provides a class for PreprocessorsPluginManager.
*/
class PreprocessorsPluginManager extends DefaultPluginManager implements PreprocessorsPluginManagerInterface {
/**
* PreprocessPluginManager constructor.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler to invoke the alter hook with.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
*/
public function __construct(
\Traversable $namespaces,
CacheBackendInterface $cache_backend,
ModuleHandlerInterface $module_handler,
protected ThemeHandlerInterface $themeHandler,
protected ThemeManagerInterface $themeManager,
) {
parent::__construct(
'Plugin/preprocessors',
$namespaces,
$module_handler,
PreprocessorInterface::class,
PreprocessorAttribute::class,
PreprocessorAnnotation::class
);
$this->setCacheBackend($cache_backend, 'preprocessors_plugins');
}
/**
* {@inheritdoc}
*/
public function getDefinitions(): array {
/** @var \Drupal\Component\Plugin\Definition\PluginDefinitionInterface[] $definitions */
$definitions = parent::getDefinitions();
// We're going to store all definitions in respective bubbles.
// This is because specific sorting within those bubbles will take place.
$moduleDefinitions = [];
$baseThemeDefinitions = [];
$activeThemeDefinitions = [];
// Get the active theme and its base themes.
$activeTheme = $this->themeManager->getActiveTheme();
$baseThemes = $activeTheme->getBaseThemeExtensions();
// We'll keep note of the discarded theme definitions as well to easily grab
// base theme definitions afterward.
$discardedThemeDefinitions = [];
// Organize our plugins.
foreach ($definitions as $definition) {
// Get the provider.
$provider = $definition[PreprocessorPluginBase::PROVIDER];
// If the provider is not a theme, then it's a module.
if (!$this->themeHandler->themeExists($provider)) {
// We do a quick check on the 'themes' property.
// If it's set to '*', it should run on all themes.
if (!isset($definition[PreprocessorPluginBase::THEMES]) || $definition[PreprocessorPluginBase::THEMES] === PreprocessorPluginBase::ALL_THEMES_VALUE) {
$moduleDefinitions[$definition['id']] = $definition;
}
else {
if (is_array($definition['themes'])) {
if (in_array($activeTheme->getName(), $definition['themes'])) {
$moduleDefinitions[$definition['id']] = $definition;
}
foreach ($baseThemes as $baseThemeName => $extension) {
if (in_array($baseThemeName, $definition['themes'])) {
$moduleDefinitions[$definition['id']] = $definition;
}
}
}
}
continue;
}
// Grab note of active theme definitions.
if ($activeTheme->getName() === $provider) {
$activeThemeDefinitions[$definition[PreprocessorPluginBase::ID]] = $definition;
}
// Keep note of discarded theme definitions. They might be base theme
// definitions.
$discardedThemeDefinitions[$provider][] = $definition;
}
// For theme definitions, we need to organize them precisely by hierarchy.
// We reverse it here because we want the root of the hierarchy first.
foreach (array_reverse($baseThemes) as $baseThemeName => $extension) {
if (isset($discardedThemeDefinitions[$baseThemeName])) {
foreach ($discardedThemeDefinitions[$baseThemeName] as $definition) {
$baseThemeDefinitions[$baseThemeName][$definition[PreprocessorPluginBase::ID]] = $definition;
}
}
}
// Now we sort all of our individual bubbles by weight.
uasort($moduleDefinitions, function ($a, $b) {
return $a[PreprocessorPluginBase::WEIGHT] - $b[PreprocessorPluginBase::WEIGHT];
});
foreach ($baseThemeDefinitions as $themeName => $defs) {
uasort($baseThemeDefinitions[$themeName], function ($a, $b) {
return $a[PreprocessorPluginBase::WEIGHT] - $b[PreprocessorPluginBase::WEIGHT];
});
}
uasort($activeThemeDefinitions, function ($a, $b) {
return $a[PreprocessorPluginBase::WEIGHT] - $b[PreprocessorPluginBase::WEIGHT];
});
// Finally, we build our new definitions array in proper order.
$definitions = $moduleDefinitions;
$definitions = array_merge($definitions, ...array_values($baseThemeDefinitions));
$definitions = array_merge(
$definitions,
$activeThemeDefinitions,
);
return $definitions;
}
/**
* {@inheritdoc}
*/
public function getPreprocessors(): array {
// Check if we have preprocessors first.
if (!$this->hasPreprocessors()) {
return [];
}
// Set a static variable. We only want to compute them once.
static $preprocessors = [];
if (!empty($preprocessors)) {
return $preprocessors;
}
// Get our definitions.
/** @var \Drupal\Component\Plugin\Definition\PluginDefinitionInterface[] $definitions */
$definitions = $this->getDefinitions();
// Loop in our definitions to set our plugins in a neat array.
// We key our plugins by the hook they are set to act on.
foreach ($definitions as $definition) {
// Get the provider type.
$providerType = $this->themeHandler->themeExists($definition[PreprocessorPluginBase::PROVIDER]) ? 'theme' : 'module';
// Since each plugin can act on multiple hooks, manage this here.
foreach ($definition[PreprocessorPluginBase::HOOKS] as $definition_hook) {
try {
$preprocessors[$definition_hook]['all'][] = $this->createInstance($definition[PreprocessorPluginBase::ID]);
$preprocessors[$definition_hook][$providerType][] = $this->createInstance($definition[PreprocessorPluginBase::ID]);
}
catch (PluginException $e) {
// @todo Add logging here.
}
}
}
return $preprocessors;
}
/**
* {@inheritdoc}
*/
public function hasPreprocessors(): bool {
static $hasPreprocessors = NULL;
if ($hasPreprocessors !== NULL) {
return $hasPreprocessors;
}
$hasPreprocessors = !empty($this->getDefinitions());
return $hasPreprocessors;
}
/**
* {@inheritdoc}
*/
protected function getDiscovery(): DiscoveryInterface {
if (!$this->discovery) {
$discovery = new AttributeDiscoveryWithAnnotations($this->subdir, $this->namespaces, $this->pluginDefinitionAttributeName, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces);
$discovery = new YamlDiscoveryDecorator($discovery, 'preprocessors', array_merge($this->moduleHandler->getModuleDirectories(), $this->themeHandler->getThemeDirectories()));
$discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
$this->discovery = $discovery;
}
return $this->discovery;
}
/**
* {@inheritdoc}
*/
protected function providerExists($provider): bool {
return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
}
}
