countdown-8.x-1.8/src/CountdownLibraryPluginManager.php

src/CountdownLibraryPluginManager.php
<?php

declare(strict_types=1);

namespace Drupal\countdown;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\countdown\Annotation\CountdownLibrary;
use Drupal\countdown\Plugin\CountdownLibraryPluginInterface;
use Psr\Log\LoggerInterface;

/**
 * @file
 * Provides the plugin manager for Countdown Library plugins.
 */

/**
 * Plugin manager for Countdown Library plugins.
 *
 * The plugin manager discovers, instantiates, and manages countdown library
 * plugins. It offers helpers for querying, validation, and building library
 * definitions used by Drupal's asset system.
 *
 * @see CountdownLibrary
 * @see CountdownLibraryPluginInterface
 * @see \Drupal\countdown\Plugin\CountdownLibraryPluginBase
 * @see plugin_api
 */
class CountdownLibraryPluginManager extends DefaultPluginManager {

  use StringTranslationTrait;

  /**
   * Channel logger for this manager.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $logger;

  /**
   * Constructs the plugin manager.
   *
   * The manager is registered as a service, so all dependencies are injected.
   * This avoids static calls and improves testability.
   *
   * @param \Traversable $namespaces
   *   A traversable of PSR-4 namespaces to search for plugin classes.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   *   The cache backend for plugin definitions.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler to support alter hooks.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger channel factory.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   The translation service for human-readable strings.
   */
  public function __construct(
    \Traversable $namespaces,
    CacheBackendInterface $cache_backend,
    ModuleHandlerInterface $module_handler,
    LoggerChannelFactoryInterface $logger_factory,
    TranslationInterface $string_translation,
  ) {
    // Register the base plugin directory, interface, and annotation class.
    parent::__construct(
      'Plugin/CountdownLibrary',
      $namespaces,
      $module_handler,
      CountdownLibraryPluginInterface::class,
      CountdownLibrary::class
    );

    // Allow other modules to alter definitions.
    $this->alterInfo('countdown_library_info');

    // Cache plugin definitions for performance.
    $this->setCacheBackend($cache_backend, 'countdown_library_plugins');

    // Initialize services.
    $this->logger = $logger_factory->get('countdown');
    $this->stringTranslation = $string_translation;
  }

  /**
   * Gets all plugin instances, optionally filtering by installation state.
   *
   * @param bool $only_installed
   *   If TRUE, only libraries that are installed will be returned.
   *
   * @return \Drupal\countdown\Plugin\CountdownLibraryPluginInterface[]
   *   An array of plugin instances keyed by plugin ID.
   */
  public function getAllPlugins(bool $only_installed = FALSE): array {
    $plugins = [];

    foreach ($this->getDefinitions() as $plugin_id => $definition) {
      try {
        /** @var \Drupal\countdown\Plugin\CountdownLibraryPluginInterface $plugin */
        $plugin = $this->createInstance($plugin_id);

        if (!$only_installed || $plugin->isInstalled()) {
          $plugins[$plugin_id] = $plugin;
        }
      }
      catch (\Throwable $e) {
        // Log and continue so one bad plugin does not block others.
        $this->logger->error(
          'Failed to create countdown plugin @id: @message',
          [
            '@id' => $plugin_id,
            '@message' => $e->getMessage(),
          ]
        );
      }
    }

    // Sort by weight for stable UI ordering.
    uasort($plugins, static function ($a, $b): int {
      return $a->getWeight() <=> $b->getWeight();
    });

    return $plugins;
  }

  /**
   * Gets installed plugin instances.
   *
   * @return \Drupal\countdown\Plugin\CountdownLibraryPluginInterface[]
   *   An array of installed plugin instances keyed by plugin ID.
   */
  public function getInstalledPlugins(): array {
    return $this->getAllPlugins(TRUE);
  }

  /**
   * Gets a specific plugin instance by ID.
   *
   * @param string $plugin_id
   *   The plugin ID.
   *
   * @return \Drupal\countdown\Plugin\CountdownLibraryPluginInterface|null
   *   The plugin instance or NULL if it could not be created.
   */
  public function getPlugin(string $plugin_id): ?CountdownLibraryPluginInterface {
    try {
      /** @var \Drupal\countdown\Plugin\CountdownLibraryPluginInterface $plugin */
      $plugin = $this->createInstance($plugin_id);
      return $plugin;
    }
    catch (\Throwable $e) {
      $this->logger->error(
        'Failed to create countdown plugin @id: @message',
        [
          '@id' => $plugin_id,
          '@message' => $e->getMessage(),
        ]
      );
      return NULL;
    }
  }

  /**
   * Builds select options for forms listing libraries.
   *
   * @param bool $only_installed
   *   If TRUE, only include installed libraries.
   * @param bool $show_versions
   *   If TRUE, append version strings to labels.
   * @param bool $group_by_type
   *   If TRUE, group by library type ("Core" vs "External").
   *
   * @return array
   *   A nested options array suitable for form API elements.
   */
  public function getPluginOptions(
    bool $only_installed = FALSE,
    bool $show_versions = TRUE,
    bool $group_by_type = FALSE,
  ): array {
    $plugins = $this->getAllPlugins($only_installed);
    $options = [];

    foreach ($plugins as $plugin_id => $plugin) {
      $label = $plugin->getLabel();

      if ($show_versions) {
        // Prefer installed version; fall back to declared minimum version.
        $version = $plugin->getInstalledVersion() ?: $plugin->getRequiredVersion();
        if ($version) {
          $label .= ' (v' . $version . ')';
        }
      }

      if ($plugin->isExperimental()) {
        $label .= ' [' . (string) $this->t('Experimental') . ']';
      }

      if ($group_by_type) {
        $type = $plugin->getType();
        $type_label = $type === 'core' ? $this->t('Core') : $this->t('External');
        $options[(string) $type_label][$plugin_id] = $label;
      }
      else {
        $options[$plugin_id] = $label;
      }
    }

    return $options;
  }

  /**
   * Gets status information for all plugins.
   *
   * @return array
   *   An array of status arrays keyed by plugin ID. Each entry contains the
   *   plugin instance under the 'plugin' key for convenience.
   */
  public function getAllPluginStatuses(): array {
    $statuses = [];

    foreach ($this->getAllPlugins() as $plugin_id => $plugin) {
      $statuses[$plugin_id] = $plugin->getStatus();
      $statuses[$plugin_id]['plugin'] = $plugin;
    }

    return $statuses;
  }

  /**
   * Validates if a given plugin can be used.
   *
   * @param string $plugin_id
   *   The plugin ID to validate.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
   *   An array of validation messages. Empty when no issues are found.
   */
  public function validatePlugin(string $plugin_id): array {
    $messages = [];
    $plugin = $this->getPlugin($plugin_id);

    if (!$plugin) {
      $messages[] = $this->t('Plugin @id does not exist.', ['@id' => $plugin_id]);
      return $messages;
    }

    if (!$plugin->isInstalled()) {
      $messages[] = $this->t('Library @label is not installed.', [
        '@label' => $plugin->getLabel(),
      ]);
    }
    elseif (!$plugin->versionMeetsRequirements()) {
      $messages[] = $this->t('Library @label does not meet version requirements.', [
        '@label' => $plugin->getLabel(),
      ]);
    }

    return $messages;
  }

  /**
   * Gets plugins that support CDN loading.
   *
   * @return \Drupal\countdown\Plugin\CountdownLibraryPluginInterface[]
   *   An array of CDN-capable plugins keyed by plugin ID.
   */
  public function getCdnCapablePlugins(): array {
    $plugins = [];

    foreach ($this->getAllPlugins() as $plugin_id => $plugin) {
      if ($plugin->getCdnConfig() !== NULL) {
        $plugins[$plugin_id] = $plugin;
      }
    }

    return $plugins;
  }

  /**
   * Clears manager and plugin-level caches.
   *
   * This clears cached plugin definitions and asks each plugin instance to
   * reset its internal caches to reflect file system changes.
   */
  public function resetAllCaches(): void {
    $this->clearCachedDefinitions();

    foreach ($this->getAllPlugins() as $plugin) {
      $plugin->resetCache();
    }
  }

  /**
   * Gets plugin definitions filtered by type.
   *
   * @param string $type
   *   The library type. Usually 'core' or 'external'.
   *
   * @return array
   *   An array of raw plugin definitions keyed by plugin ID.
   */
  public function getDefinitionsByType(string $type): array {
    $definitions = [];

    foreach ($this->getDefinitions() as $plugin_id => $definition) {
      if (($definition['type'] ?? 'external') === $type) {
        $definitions[$plugin_id] = $definition;
      }
    }

    return $definitions;
  }

  /**
   * Checks if a plugin exists.
   *
   * @param string $plugin_id
   *   The plugin ID.
   *
   * @return bool
   *   TRUE if the plugin exists, FALSE otherwise.
   */
  public function hasPlugin(string $plugin_id): bool {
    return $this->hasDefinition($plugin_id);
  }

  /**
   * Gets installed plugins that meet their version requirements.
   *
   * @return \Drupal\countdown\Plugin\CountdownLibraryPluginInterface[]
   *   An array of compatible plugin instances keyed by plugin ID.
   */
  public function getCompatiblePlugins(): array {
    $plugins = [];

    foreach ($this->getInstalledPlugins() as $plugin_id => $plugin) {
      if ($plugin->versionMeetsRequirements()) {
        $plugins[$plugin_id] = $plugin;
      }
    }

    return $plugins;
  }

  /**
   * Builds library definitions for hook_library_info_build().
   *
   * @param bool $minified
   *   Whether to use minified assets when available.
   *
   * @return array
   *   An array of library definitions keyed by a machine name. The caller is
   *   responsible for prefixing with the 'countdown.' or similar schema.
   */
  public function buildLibraryDefinitions(bool $minified = TRUE): array {
    $definitions = [];

    foreach ($this->getInstalledPlugins() as $plugin_id => $plugin) {
      $lib_name = 'countdown.' . $plugin_id;
      $definitions[$lib_name] = $plugin->buildLibraryDefinition($minified);
    }

    return $definitions;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc