UpdateManager.php

Same filename in other branches
  1. 9 core/modules/update/src/UpdateManager.php
  2. 10 core/modules/update/src/UpdateManager.php
  3. 11.x core/modules/update/src/UpdateManager.php

Namespace

Drupal\update

File

core/modules/update/src/UpdateManager.php

View source
<?php

namespace Drupal\update;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\ProjectInfo;

/**
 * Default implementation of UpdateManagerInterface.
 */
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 a 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. This is left optional for BC reasons, but the
     *   optional usage is deprecated and will become required in Drupal 9.0.0.
     */
    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 = NULL) {
        $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 = [];
        if ($extension_list_module === NULL) {
            @trigger_error('Invoking the UpdateManager constructor without the module extension list parameter is deprecated in Drupal 8.8.0 and will no longer be supported in Drupal 9.0.0. The extension list parameter is now required in the ConfigImporter constructor. See https://www.drupal.org/node/2943918', E_USER_DEPRECATED);
            $extension_list_module = \Drupal::service('extension.list.module');
        }
        $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;
            }
        }
    }

}

Classes

Title Deprecated Summary
UpdateManager Default implementation of UpdateManagerInterface.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.