View source
<?php
namespace Drupal\markdown\PluginManager;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\markdown\Annotation\InstallablePlugin;
use Drupal\markdown\Annotation\InstallableLibrary;
use Drupal\markdown\Annotation\InstallableRequirement;
use Drupal\markdown\Exception\MarkdownUnexpectedValueException;
use Drupal\markdown\Plugin\Markdown\InstallablePluginInterface;
use Drupal\markdown\Traits\NormalizeTrait;
use Drupal\markdown\Util\Composer;
use Drupal\markdown\Util\Error;
use Drupal\markdown\Util\Semver;
use Drupal\markdown\Util\SortArray;
use Psr\Log\LoggerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class InstallablePluginManager extends DefaultPluginManager implements InstallablePluginManagerInterface {
use ContainerAwareTrait;
use NormalizeTrait;
use StringTranslationTrait;
protected $cacheContexts = [];
protected $cacheMaxAge = Cache::PERMANENT;
protected static $runtimeDefinitions = [];
protected $configFactory;
protected $logger;
public function __construct($subDirectory, \Traversable $namespaces, ConfigFactoryInterface $configFactory, LoggerInterface $logger, ModuleHandlerInterface $module_handler, $plugin_interface = NULL, $plugin_definition_annotation_name = 'Drupal\\Component\\Annotation\\Plugin', array $additional_annotation_namespaces = []) {
parent::__construct($subDirectory, $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name, $additional_annotation_namespaces);
$this->configFactory = $configFactory;
$this->logger = $logger;
}
public function all(array $configuration = [], $includeFallback = FALSE) {
$definitions = $this
->getDefinitions($includeFallback);
uasort($definitions, function (InstallablePlugin $a, InstallablePlugin $b) {
if ($a->weight === $b->weight) {
return 0;
}
return $a->weight < $b->weight ? -1 : 1;
});
return array_map(function (InstallablePlugin $definition) use ($configuration) {
$id = $definition
->getId();
return $this
->createInstance($id, isset($configuration[$id]) ? $configuration[$id] : $configuration);
}, $definitions);
}
protected function alterDefinition(InstallablePlugin $definition, $runtime = FALSE) {
}
protected function alterDefinitions(&$definitions, $runtime = FALSE) {
foreach ($definitions as $definition) {
if ($definition instanceof InstallablePlugin) {
$this
->alterDefinition($definition, $runtime);
}
}
if ($hook = $this->alterHook) {
if ($runtime) {
$hook = "_runtime";
}
$this->moduleHandler
->alter($hook, $definitions);
}
}
public function clearCachedDefinitions() {
parent::clearCachedDefinitions();
static::$runtimeDefinitions = [];
}
protected function convertInstalledToLibraries(InstallablePlugin $plugin) {
if (empty($installed = $plugin->installed)) {
return;
}
$installs = [];
foreach ((array) $plugin->installed as $key => $value) {
$object = NULL;
if ($value !== TRUE) {
$object = static::normalizeClassName(is_string($key) && strpos($key, '\\') !== FALSE ? $key : $value);
$installs[$object] = is_array($value) ? $value : [];
}
}
foreach ($installs as $class => $definition) {
$library = InstallableLibrary::create()
->merge($definition);
$library->object = $class;
$plugin->libraries[] = $library;
}
unset($plugin->installed);
$library = reset($plugin->libraries);
if (($url = $plugin->url) && !$library->url) {
$library->url = $url;
unset($plugin->url);
}
if (($version = $plugin->version) && !$library->version) {
$library->version = $version;
unset($plugin->version);
}
if ($versionConstraint = $plugin->versionConstraint) {
$requirement = new InstallableRequirement();
$requirement->constraints['Version'] = [
'name' => $plugin
->id(),
'constraint' => $versionConstraint,
];
$library->requirements[] = $requirement;
unset($plugin->versionConstraint);
}
}
public function createInstance($plugin_id, array $configuration = []) {
$instance = parent::createInstance($plugin_id, $configuration);
if ($instance instanceof ContainerAwareInterface) {
$instance
->setContainer($this
->getContainer());
}
return $instance;
}
protected function findDefinitions() {
$definitions = $this
->getDiscovery()
->getDefinitions();
foreach ($definitions as $plugin_id => $definition) {
if (($provider = $definition
->getProvider()) && !in_array($provider, [
'core',
'component',
]) && !$this
->providerExists($provider)) {
unset($definitions[$plugin_id]);
}
}
foreach ($definitions as $plugin_id => &$definition) {
$this
->processDefinition($definition, $plugin_id);
}
$this
->alterDefinitions($definitions);
return $definitions;
}
public function firstInstalledPluginId() {
return current(array_keys($this
->installedDefinitions())) ?: $this
->getFallbackPluginId();
}
public function getCacheContexts() {
return $this->cacheContexts;
}
protected function getCachedDefinitions($runtime = FALSE) {
$cacheKey = $this
->getCacheKey($runtime);
if ($runtime) {
if (!isset(static::$runtimeDefinitions[static::class]) && ($cache = $this
->cacheGet($cacheKey))) {
static::$runtimeDefinitions[static::class] = $cache->data;
}
return static::$runtimeDefinitions[static::class];
}
else {
if (!isset($this->definitions) && ($cache = $this
->cacheGet($cacheKey))) {
$this->definitions = $cache->data;
}
return $this->definitions;
}
}
public function getCacheKey($runtime = FALSE) {
$cacheKey = $this->cacheKey;
if ($runtime) {
$request = \Drupal::request();
if ($request->attributes
->has(RouteObjectInterface::ROUTE_OBJECT)) {
$cacheKey .= ':runtime:' . \Drupal::theme()
->getActiveTheme()
->getName();
}
else {
$cacheKey .= ':runtime';
}
}
return $cacheKey;
}
public function getCacheMaxAge() {
return $this->cacheMaxAge;
}
public function getCacheTags() {
return $this->cacheTags;
}
public function getContainer() {
return $this->container instanceof ContainerInterface ? $this->container : \Drupal::getContainer();
}
public function getDefinitionByClassName($className) {
$className = static::normalizeClassName($className);
foreach ($this
->getDefinitions() as $definition) {
if ($definition
->getClass() === $className) {
return $definition;
}
}
}
public function getDefinitionByLibraryId($libraryId) {
foreach ($this
->getDefinitions() as $definition) {
foreach ($definition->libraries as $library) {
if ($library
->getId() === (string) $libraryId) {
return $definition;
}
}
}
}
public function getDefinitions($includeFallback = TRUE) {
$definitions = $this
->getRuntimeDefinitions();
if ($includeFallback) {
return $definitions;
}
unset($definitions[$this
->getFallbackPluginId()]);
return $definitions;
}
public abstract function getFallbackPluginId($plugin_id = NULL, array $configuration = []);
protected function getRuntimeDefinitions() {
if (!array_key_exists(static::class, static::$runtimeDefinitions)) {
static::$runtimeDefinitions[static::class] = NULL;
}
static::$runtimeDefinitions[static::class] = $this
->getCachedDefinitions(TRUE);
if (!isset(static::$runtimeDefinitions[static::class])) {
static::$runtimeDefinitions[static::class] = parent::getDefinitions();
foreach (static::$runtimeDefinitions[static::class] as $definition) {
$definition
->validate(TRUE);
}
$this
->alterDefinitions(static::$runtimeDefinitions[static::class], TRUE);
try {
static::normalizeCallables(static::$runtimeDefinitions[static::class]);
} catch (MarkdownUnexpectedValueException $exception) {
$plugin_id = array_reverse($exception
->getParents())[0];
$annotation = array_reverse(explode('\\', $this->pluginDefinitionAnnotationName))[0];
throw new InvalidPluginDefinitionException($plugin_id, sprintf('Invalid callback defined in @%s. %s.', $annotation, $exception
->getMessage()), 0, isset($e) ? $e : NULL);
}
foreach (static::$runtimeDefinitions[static::class] as $plugin_id => $definition) {
$definition
->validate(TRUE);
}
$this
->sortDefinitions(static::$runtimeDefinitions[static::class]);
$this
->setCachedDefinitions(static::$runtimeDefinitions[static::class], TRUE);
}
$this->definitions = static::$runtimeDefinitions[static::class];
return $this->definitions;
}
protected function handlePluginNotFound($plugin_id, array $configuration) {
$fallback_id = $this
->getFallbackPluginId($plugin_id, $configuration);
$configuration['original_plugin_id'] = $plugin_id;
return $this
->getFactory()
->createInstance($fallback_id, $configuration);
}
public function installed(array $configuration = []) {
return array_map(function (InstallablePlugin $definition) use ($configuration) {
$id = $definition
->getId();
return $this
->createInstance($id, isset($configuration[$id]) ? $configuration[$id] : $configuration);
}, $this
->installedDefinitions());
}
public function installedDefinitions() {
return array_filter($this
->getDefinitions(FALSE), function ($definition) {
return $definition
->getId() !== $this
->getFallbackPluginId() && $definition
->isInstalled();
});
}
public function processDefinition(&$definition, $pluginId) {
if (!$definition instanceof InstallablePlugin) {
return;
}
$definition
->setClass(static::normalizeClassName($definition
->getClass()));
$this
->convertInstalledToLibraries($definition);
if (!$definition->libraries && !$definition->requirements && !$definition->runtimeRequirements && !$definition->requirementViolations) {
$definition->libraries[] = InstallableLibrary::create($definition);
}
$preferred = FALSE;
$preferredWeight = -1;
$seenIds = [];
foreach ($definition->libraries as $key => $library) {
$id = $library
->getId();
if (!isset($seenIds[$id])) {
$seenIds[$id] = $library;
}
else {
unset($definition->libraries[$key]);
}
$this
->processLibraryDefinition($definition, $library, $preferred);
$preferredWeight = min($preferredWeight, $library->weight);
}
if (!$preferred && ($library = reset($definition->libraries))) {
$library->preferred = TRUE;
$library->weight = $preferredWeight;
}
$this
->sortDefinitions($definition->libraries);
if ($library = $definition
->getInstalledLibrary() ?: $definition
->getPreferredLibrary()) {
$definition
->merge($library, [
'ui',
'weight',
]);
if (!$definition->url && ($url = $library
->getUrl())) {
$definition->url = $url
->toString();
}
}
}
protected function createObjectRequirement(InstallablePlugin $definition, InstallableLibrary $library) {
return $library
->createObjectRequirement($definition);
}
protected function processLibraryDefinition(InstallablePlugin $definition, InstallableLibrary $library, &$preferred = FALSE) {
if (!$preferred && $library->preferred) {
$preferred = TRUE;
}
if ($library->object) {
$library->object = static::normalizeClassName($library->object);
if ($requirement = $this
->createObjectRequirement($definition, $library)) {
array_unshift($library->requirements, $requirement);
}
}
if ($versionConstraint = $library->versionConstraint) {
$requirement = new InstallableRequirement();
$requirement->constraints['Version'] = $versionConstraint;
$library->requirements[] = $requirement;
unset($library->versionConstraint);
}
$versionDefinition = NULL;
if (!empty($library->version) && is_string($library->version) && !Semver::isValid($library->version)) {
$versionDefinition = static::normalizeClassName($library->version);
unset($library->version);
}
$versionRequirement = NULL;
if ($library->requirements) {
foreach ($library->requirements as $key => $requirement) {
if ($requirement instanceof InstallableLibrary) {
$requirement = $requirement
->createObjectRequirement();
}
if (!isset($requirement->value) && !isset($requirement->callback) && count($requirement->constraints) === 1 && key($requirement->constraints) === 'Version' && !empty($requirement->constraints['Version'])) {
$versionRequirement = $requirement;
unset($library->requirements[$key]);
continue;
}
if (in_array($requirement
->getType(), [
'parser',
'extension',
], TRUE)) {
$library->runtimeRequirements[] = $requirement;
unset($library->requirements[$key]);
continue;
}
Error::suppress(function () use ($requirement, $library) {
foreach ($requirement
->validate() as $violation) {
$key = (string) $violation
->getMessage();
if (!isset($library->requirementViolations[$key])) {
$library->requirementViolations[$key] = $violation
->getMessage();
}
}
});
}
}
if (isset($versionDefinition)) {
if (!$versionRequirement) {
$versionRequirement = new InstallableRequirement();
$versionRequirement->constraints = [
'Version' => [
'name' => $definition
->id(),
],
];
}
if (defined($versionDefinition) && ($version = constant($versionDefinition))) {
$versionRequirement->value = $version;
}
elseif (is_callable($versionDefinition) && ($version = call_user_func_array($versionDefinition, [
$library,
$definition,
]))) {
$versionRequirement->value = $version;
}
elseif ($library->object && ($version = Composer::getVersionFromClass($library->object))) {
$versionRequirement->value = $version;
}
$violations = Error::suppress(function () use ($versionRequirement) {
return $versionRequirement
->validate();
});
if ($violations && $violations
->count()) {
foreach ($violations as $violation) {
$key = (string) $violation
->getMessage();
if (!isset($library->requirementViolations[$key])) {
$library->requirementViolations[$key] = $violation
->getMessage();
}
}
}
elseif ($violations !== null) {
$library->version = $versionRequirement->value;
}
}
}
public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = []) {
$cache_tags[] = $cache_key;
$cache_tags[] = "{$cache_key}:runtime";
$themeHandler = \Drupal::service('theme_handler');
foreach (array_keys($themeHandler
->listInfo()) as $theme) {
$cache_tags[] = "{$cache_key}:runtime:{$theme}";
}
parent::setCacheBackend($cache_backend, $cache_key, array_unique($cache_tags));
}
protected function setCachedDefinitions($definitions, $runtime = FALSE) {
$cacheKey = $this
->getCacheKey($runtime);
$this
->cacheSet($cacheKey, $definitions, Cache::PERMANENT, [
$cacheKey,
]);
if ($runtime) {
static::$runtimeDefinitions[static::class] = $definitions;
}
else {
$this->definitions = $definitions;
}
}
protected function sortDefinitions(array &$definitions, array $properties = [
'weight',
'label',
]) {
SortArray::multisortProperties($definitions, $properties);
}
}