countdown-8.x-1.8/src/Service/CountdownLibraryDiscovery.php

src/Service/CountdownLibraryDiscovery.php
<?php

declare(strict_types=1);

namespace Drupal\countdown\Service;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\countdown\CountdownConstants;
use Drupal\countdown\CountdownLibraryPluginManager;
use Drupal\countdown\Utility\ConfigAccessor;
use Psr\Log\LoggerInterface;

/**
 * Service for discovering countdown libraries using Plugin System.
 *
 * This service provides comprehensive library discovery functionality,
 * integrating with the plugin system to find, validate, and manage
 * countdown libraries across the Drupal installation.
 *
 * @package Drupal\countdown\Service
 */
class CountdownLibraryDiscovery implements CountdownLibraryDiscoveryInterface {

  use StringTranslationTrait;

  /**
   * The configuration factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The plugin manager.
   *
   * @var \Drupal\countdown\CountdownLibraryPluginManager
   */
  protected CountdownLibraryPluginManager $pluginManager;

  /**
   * The cache backend.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected CacheBackendInterface $cache;

  /**
   * The file system service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected FileSystemInterface $fileSystem;

  /**
   * The logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $logger;

  /**
   * The configuration accessor.
   *
   * @var \Drupal\countdown\Utility\ConfigAccessor
   */
  protected ConfigAccessor $configAccessor;

  /**
   * The site path.
   *
   * @var string
   */
  protected string $sitePath;

  /**
   * Static cache for discovered libraries.
   *
   * @var array|null
   */
  protected ?array $discoveredLibraries = NULL;

  /**
   * Static cache for library paths.
   *
   * @var array
   */
  protected array $libraryPaths = [];

  /**
   * Static cache for detected versions.
   *
   * @var array
   */
  protected array $detectedVersions = [];

  /**
   * Constructs a CountdownLibraryDiscovery object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The configuration factory.
   * @param \Drupal\countdown\CountdownLibraryPluginManager $plugin_manager
   *   The plugin manager.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache backend.
   * @param \Drupal\Core\File\FileSystemInterface $file_system
   *   The file system service.
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   The string translation service.
   * @param string $site_path
   *   The site path.
   */
  public function __construct(
    ConfigFactoryInterface $config_factory,
    CountdownLibraryPluginManager $plugin_manager,
    CacheBackendInterface $cache,
    FileSystemInterface $file_system,
    LoggerInterface $logger,
    TranslationInterface $string_translation,
    string $site_path,
  ) {
    $this->pluginManager = $plugin_manager;
    $this->cache = $cache;
    $this->fileSystem = $file_system;
    $this->logger = $logger;
    $this->stringTranslation = $string_translation;
    $this->sitePath = $site_path;

    // Create config accessor.
    $this->configAccessor = new ConfigAccessor($config_factory);
  }

  /**
   * {@inheritdoc}
   */
  public function discover(bool $reset = FALSE): array {
    // Use static cache if available and not resetting.
    if (!$reset && $this->discoveredLibraries !== NULL) {
      return $this->discoveredLibraries;
    }

    // Try to get from cache.
    $cache_id = 'countdown:available_libraries:' . $this->sitePath;

    if (!$reset && $cached = $this->cache->get($cache_id)) {
      $this->discoveredLibraries = $cached->data;
      return $this->discoveredLibraries;
    }

    // Perform discovery using plugin system.
    $this->discoveredLibraries = $this->performDiscovery();

    // Cache the results.
    $this->cache->set(
      $cache_id,
      $this->discoveredLibraries,
      time() + $this->configAccessor->getCacheLifetime(),
      [CountdownConstants::CACHE_TAG_DISCOVERY]
    );

    return $this->discoveredLibraries;
  }

  /**
   * {@inheritdoc}
   */
  public function getLibrary(string $library_id): ?array {
    $libraries = $this->discover();
    return $libraries[$library_id] ?? NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getAvailableLibraries(): array {
    $libraries = $this->discover();
    $available = [];

    foreach ($libraries as $id => $info) {
      if ($info['installed']) {
        $available[$id] = $info;
      }
    }

    return $available;
  }

  /**
   * {@inheritdoc}
   */
  public function isInstalled(string $library_id): bool {
    $library = $this->getLibrary($library_id);
    return $library && $library['installed'];
  }

  /**
   * {@inheritdoc}
   */
  public function getLibraryPath(string $library_id): ?string {
    // Check cache first.
    if (isset($this->libraryPaths[$library_id])) {
      return $this->libraryPaths[$library_id];
    }

    $library = $this->getLibrary($library_id);
    $path = $library ? $library['path'] : NULL;

    // Cache the result.
    $this->libraryPaths[$library_id] = $path;

    return $path;
  }

  /**
   * {@inheritdoc}
   */
  public function getLibraryAssets(string $library_id): array {
    $plugin = $this->pluginManager->getPlugin($library_id);

    if (!$plugin || !$plugin->isInstalled()) {
      return [];
    }

    // Core library assets are handled by static library definition.
    if ($library_id === 'countdown') {
      return [];
    }

    $variant = $this->configAccessor->getBuildVariant();

    // Get all assets - JS and CSS are always loaded.
    $assets = $plugin->getAssets($variant);

    return $assets;
  }

  /**
   * {@inheritdoc}
   */
  public function getInstalledVersion(string $library_id): ?string {
    // Check cache first.
    if (isset($this->detectedVersions[$library_id])) {
      return $this->detectedVersions[$library_id] !== FALSE ? $this->detectedVersions[$library_id] : NULL;
    }

    $plugin = $this->pluginManager->getPlugin($library_id);

    if (!$plugin || !$plugin->isInstalled()) {
      $this->detectedVersions[$library_id] = FALSE;
      return NULL;
    }

    $version = $plugin->getInstalledVersion();
    $this->detectedVersions[$library_id] = $version ?: FALSE;

    return $version;
  }

  /**
   * {@inheritdoc}
   */
  public function versionMeetsRequirements(string $library_id): bool {
    $plugin = $this->pluginManager->getPlugin($library_id);

    if (!$plugin || !$plugin->isInstalled()) {
      return FALSE;
    }

    return $plugin->versionMeetsRequirements();
  }

  /**
   * {@inheritdoc}
   */
  public function getLibraryStatus(string $library_id): array {
    $plugin = $this->pluginManager->getPlugin($library_id);

    if (!$plugin) {
      return [
        'installed' => FALSE,
        'version_status' => 'unknown',
        'messages' => [$this->t('Library not found in registry.')],
        'severity' => 'error',
      ];
    }

    return $plugin->getStatus();
  }

  /**
   * {@inheritdoc}
   */
  public function clearCache(): void {
    $cache_id = 'countdown:available_libraries:' . $this->sitePath;
    $this->cache->delete($cache_id);

    // Clear static caches.
    $this->discoveredLibraries = NULL;
    $this->libraryPaths = [];
    $this->detectedVersions = [];

    // Also reset plugin manager cache.
    $this->pluginManager->resetAllCaches();

    if ($this->configAccessor->isDebugMode()) {
      $this->logger->debug('Countdown library discovery cache cleared.');
    }
  }

  /**
   * Perform the actual library discovery using plugin system.
   *
   * @return array
   *   Array of discovered libraries with their status.
   */
  protected function performDiscovery(): array {
    $libraries = [];

    // Get all plugins from the plugin manager.
    $all_plugins = $this->pluginManager->getAllPlugins();

    foreach ($all_plugins as $plugin_id => $plugin) {
      $library_info = [
        'id' => $plugin_id,
        'label' => $plugin->getLabel(),
        'description' => $plugin->getDescription(),
        'type' => $plugin->getType(),
        'installed' => $plugin->isInstalled(),
        'path' => $plugin->getLibraryPath(),
        'version' => $plugin->getRequiredVersion(),
        'installed_version' => NULL,
        'version_match' => FALSE,
        'errors' => [],
        'warnings' => [],
        'homepage' => $plugin->getHomepage(),
        'repository' => $plugin->getRepository(),
        'author' => $plugin->getAuthor(),
        'license' => $plugin->getLicense(),
        'experimental' => $plugin->isExperimental(),
        'weight' => $plugin->getWeight(),
        'dependencies' => $plugin->getDependencies(),
        'cdn_capable' => $plugin->getCdnConfig() !== NULL,
        'npm_package' => $plugin->getNpmPackage(),
      ];

      // Get installed version if library is installed.
      if ($plugin->isInstalled()) {
        $installed_version = $plugin->getInstalledVersion();
        if ($installed_version) {
          $library_info['installed_version'] = $installed_version;
          $library_info['version_match'] = $plugin->versionMeetsRequirements();

          if (!$library_info['version_match'] && $library_info['version']) {
            $library_info['warnings'][] = $this->t('Version @installed does not meet requirement @required', [
              '@installed' => $installed_version,
              '@required' => $library_info['version'],
            ]);
          }
        }
        else {
          $library_info['warnings'][] = $this->t('Could not detect installed version.');
        }

        // Log successful discovery if debug enabled.
        if ($this->configAccessor->isDebugMode()) {
          $this->logger->debug('Discovered library @library at @path (version: @version)', [
            '@library' => $plugin_id,
            '@path' => $plugin->getLibraryPath(),
            '@version' => $installed_version ?: 'unknown',
          ]);
        }
      }
      else {
        // Library not installed.
        if ($plugin->getType() === 'external') {
          $library_info['errors'][] = $this->t('Library is not installed.');

          // Add installation hints.
          if ($npm_package = $plugin->getNpmPackage()) {
            $library_info['install_hint'] = $this->t('Install with: npm install @package', [
              '@package' => $npm_package,
            ]);
          }
          elseif ($homepage = $plugin->getHomepage()) {
            $library_info['install_hint'] = $this->t('Download from: @url', [
              '@url' => $homepage,
            ]);
          }
        }

        if ($this->configAccessor->isDebugMode()) {
          $this->logger->debug('Library @library is not installed', [
            '@library' => $plugin_id,
          ]);
        }
      }

      // Add validation messages if any.
      $validation_messages = $this->pluginManager->validatePlugin($plugin_id);
      if (!empty($validation_messages)) {
        $library_info['errors'] = array_merge($library_info['errors'], $validation_messages);
      }

      // Determine overall status.
      if (!empty($library_info['errors'])) {
        $library_info['status'] = 'error';
      }
      elseif (!empty($library_info['warnings'])) {
        $library_info['status'] = 'warning';
      }
      else {
        $library_info['status'] = 'ok';
      }

      $libraries[$plugin_id] = $library_info;
    }

    // Sort libraries by weight and then by label.
    uasort($libraries, function ($a, $b) {
      $weight_compare = $a['weight'] <=> $b['weight'];
      if ($weight_compare !== 0) {
        return $weight_compare;
      }
      return strcasecmp($a['label'], $b['label']);
    });

    return $libraries;
  }

}

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

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