You are here

class UpdateManager in Drupal 10

Same name and namespace in other branches
  1. 8 core/modules/update/src/UpdateManager.php \Drupal\update\UpdateManager
  2. 9 core/modules/update/src/UpdateManager.php \Drupal\update\UpdateManager

Default implementation of UpdateManagerInterface.

Hierarchy

  • class \Drupal\update\UpdateManager implements \Drupal\update\UpdateManagerInterface uses \Drupal\Core\DependencyInjection\DependencySerializationTrait, \Drupal\Core\StringTranslation\StringTranslationTrait

Expanded class hierarchy of UpdateManager

1 string reference to 'UpdateManager'
update.services.yml in core/modules/update/update.services.yml
core/modules/update/update.services.yml
1 service uses UpdateManager
update.manager in core/modules/update/update.services.yml
Drupal\update\UpdateManager

File

core/modules/update/src/UpdateManager.php, line 18

Namespace

Drupal\update
View source
class UpdateManager implements UpdateManagerInterface {
  use DependencySerializationTrait;
  use StringTranslationTrait;

  /**
   * The update settings.
   *
   * @var \Drupal\Core\Config\Config
   */
  protected $updateSettings;

  /**
   * Module Handler Service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Update Processor Service.
   *
   * @var \Drupal\update\UpdateProcessorInterface
   */
  protected $updateProcessor;

  /**
   * An array of installed and enabled projects.
   *
   * @var array
   */
  protected $projects;

  /**
   * The key/value store.
   *
   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
   */
  protected $keyValueStore;

  /**
   * Update available releases key/value store.
   *
   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
   */
  protected $availableReleasesTempStore;

  /**
   * The theme handler.
   *
   * @var \Drupal\Core\Extension\ThemeHandlerInterface
   */
  protected $themeHandler;

  /**
   * The module extension list.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected $moduleExtensionList;

  /**
   * Constructs an UpdateManager.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The Module Handler service
   * @param \Drupal\update\UpdateProcessorInterface $update_processor
   *   The Update Processor service.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
   *   The translation service.
   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_expirable_factory
   *   The expirable key/value factory.
   * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
   *   The theme handler.
   * @param \Drupal\Core\Extension\ModuleExtensionList|null $extension_list_module
   *   The module extension list.
   */
  public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, UpdateProcessorInterface $update_processor, TranslationInterface $translation, KeyValueFactoryInterface $key_value_expirable_factory, ThemeHandlerInterface $theme_handler, ModuleExtensionList $extension_list_module) {
    $this->updateSettings = $config_factory
      ->get('update.settings');
    $this->moduleHandler = $module_handler;
    $this->updateProcessor = $update_processor;
    $this->stringTranslation = $translation;
    $this->keyValueStore = $key_value_expirable_factory
      ->get('update');
    $this->themeHandler = $theme_handler;
    $this->availableReleasesTempStore = $key_value_expirable_factory
      ->get('update_available_releases');
    $this->projects = [];
    $this->moduleExtensionList = $extension_list_module;
  }

  /**
   * {@inheritdoc}
   */
  public function refreshUpdateData() {

    // Since we're fetching new available update data, we want to clear
    // of both the projects we care about, and the current update status of the
    // site. We do *not* want to clear the cache of available releases just yet,
    // since that data (even if it's stale) can be useful during
    // \Drupal\update\UpdateManager::getProjects(); for example, to modules
    // that implement hook_system_info_alter() such as cvs_deploy.
    $this->keyValueStore
      ->delete('update_project_projects');
    $this->keyValueStore
      ->delete('update_project_data');
    $projects = $this
      ->getProjects();

    // Now that we have the list of projects, we should also clear the available
    // release data, since even if we fail to fetch new data, we need to clear
    // out the stale data at this point.
    $this->availableReleasesTempStore
      ->deleteAll();
    foreach ($projects as $project) {
      $this->updateProcessor
        ->createFetchTask($project);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getProjects() {
    if (empty($this->projects)) {

      // Retrieve the projects from storage, if present.
      $this->projects = $this
        ->projectStorage('update_project_projects');
      if (empty($this->projects)) {

        // Still empty, so we have to rebuild.
        $module_data = $this->moduleExtensionList
          ->reset()
          ->getList();
        $theme_data = $this->themeHandler
          ->rebuildThemeData();
        $project_info = new ProjectInfo();
        $project_info
          ->processInfoList($this->projects, $module_data, 'module', TRUE);
        $project_info
          ->processInfoList($this->projects, $theme_data, 'theme', TRUE);
        if ($this->updateSettings
          ->get('check.disabled_extensions')) {
          $project_info
            ->processInfoList($this->projects, $module_data, 'module', FALSE);
          $project_info
            ->processInfoList($this->projects, $theme_data, 'theme', FALSE);
        }

        // Allow other modules to alter projects before fetching and comparing.
        $this->moduleHandler
          ->alter('update_projects', $this->projects);

        // Store the site's project data for at most 1 hour.
        $this->keyValueStore
          ->setWithExpire('update_project_projects', $this->projects, 3600);
      }
    }
    return $this->projects;
  }

  /**
   * {@inheritdoc}
   */
  public function projectStorage($key) {
    $projects = [];

    // On certain paths, we should clear the data and recompute the projects for
    // update status of the site to avoid presenting stale information.
    $route_names = [
      'update.theme_update',
      'system.modules_list',
      'system.theme_install',
      'update.module_update',
      'update.module_install',
      'update.status',
      'update.report_update',
      'update.report_install',
      'update.settings',
      'system.status',
      'update.manual_status',
      'update.confirmation_page',
      'system.themes_page',
    ];
    if (in_array(\Drupal::routeMatch()
      ->getRouteName(), $route_names)) {
      $this->keyValueStore
        ->delete($key);
    }
    else {
      $projects = $this->keyValueStore
        ->get($key, []);
    }
    return $projects;
  }

  /**
   * {@inheritdoc}
   */
  public function fetchDataBatch(&$context) {
    if (empty($context['sandbox']['max'])) {
      $context['finished'] = 0;
      $context['sandbox']['max'] = $this->updateProcessor
        ->numberOfQueueItems();
      $context['sandbox']['progress'] = 0;
      $context['message'] = $this
        ->t('Checking available update data ...');
      $context['results']['updated'] = 0;
      $context['results']['failures'] = 0;
      $context['results']['processed'] = 0;
    }

    // Grab another item from the fetch queue.
    for ($i = 0; $i < 5; $i++) {
      if ($item = $this->updateProcessor
        ->claimQueueItem()) {
        if ($this->updateProcessor
          ->processFetchTask($item->data)) {
          $context['results']['updated']++;
          $context['message'] = $this
            ->t('Checked available update data for %title.', [
            '%title' => $item->data['info']['name'],
          ]);
        }
        else {
          $context['message'] = $this
            ->t('Failed to check available update data for %title.', [
            '%title' => $item->data['info']['name'],
          ]);
          $context['results']['failures']++;
        }
        $context['sandbox']['progress']++;
        $context['results']['processed']++;
        $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
        $this->updateProcessor
          ->deleteQueueItem($item);
      }
      else {

        // If the queue is currently empty, we're done. It's possible that
        // another thread might have added new fetch tasks while we were
        // processing this batch. In that case, the usual 'finished' math could
        // get confused, since we'd end up processing more tasks that we thought
        // we had when we started and initialized 'max' with numberOfItems(). By
        // forcing 'finished' to be exactly 1 here, we ensure that batch
        // processing is terminated.
        $context['finished'] = 1;
        return;
      }
    }
  }

}

Members