Commit f8a6d960 authored by catch's avatar catch
Browse files

Issue #2917600 by tedbow, alexpott, catch, anthonyf, xjm, Alan D., andypost,...

Issue #2917600 by tedbow, alexpott, catch, anthonyf, xjm, Alan D., andypost, Berdir, moshe weitzman: update_fix_compatibility() puts sites into unrecoverable state
parent 55b98f51
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -80,12 +80,16 @@
 * Loads .install files for installed modules to initialize the update system.
 */
function drupal_load_updates() {
  /** @var \Drupal\Core\Extension\ModuleExtensionList $extension_list_module */
  $extension_list_module = \Drupal::service('extension.list.module');
  foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as $module => $schema_version) {
    if ($extension_list_module->exists($module) && !$extension_list_module->checkIncompatibility($module)) {
      if ($schema_version > -1) {
        module_load_install($module);
      }
    }
  }
}

/**
 * Loads the installation profile, extracting its defined distribution name.
+8 −1
Original line number Diff line number Diff line
@@ -14,8 +14,13 @@

/**
 * Disables any extensions that are incompatible with the current core version.
 *
 * @deprecated in Drupal 8.8.4 and is removed from Drupal 9.0.0.
 *
 * @see https://www.drupal.org/node/3026100
 */
function update_fix_compatibility() {
  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.8.4 and will be removed before Drupal 9.0.0. There is no replacement. See https://www.drupal.org/node/3026100', E_USER_DEPRECATED);
  // Fix extension objects if the update is being done via Drush 8. In non-Drush
  // environments this will already be fixed by the UpdateKernel this point.
  UpdateKernel::fixSerializedExtensionObjects(\Drupal::getContainer());
@@ -306,9 +311,11 @@ function update_get_update_list() {
  $ret = ['system' => []];

  $modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE);
  /** @var \Drupal\Core\Extension\ExtensionList $extension_list */
  $extension_list = \Drupal::service('extension.list.module');
  foreach ($modules as $module => $schema_version) {
    // Skip uninstalled and incompatible modules.
    if ($schema_version == SCHEMA_UNINSTALLED || update_check_incompatibility($module)) {
    if ($schema_version == SCHEMA_UNINSTALLED || $extension_list->checkIncompatibility($module)) {
      continue;
    }
    // Display a requirements error if the user somehow has a schema version
+17 −0
Original line number Diff line number Diff line
@@ -563,4 +563,21 @@ protected function createExtensionInfo(Extension $extension) {
    return $info;
  }

  /**
   * Tests the compatibility of an extension.
   *
   * @param string $name
   *   The extension name to check.
   *
   * @return bool
   *   TRUE if the extension is incompatible and FALSE if not.
   *
   * @throws \Drupal\Core\Extension\Exception\UnknownExtensionException
   *   If there is no extension with the supplied name.
   */
  public function checkIncompatibility($name) {
    $extension = $this->get($name);
    return $extension->info['core_incompatible'] || (isset($extension->info['php']) && version_compare(phpversion(), $extension->info['php']) < 0);
  }

}
+0 −1
Original line number Diff line number Diff line
@@ -144,7 +144,6 @@ public function handle($op, Request $request) {
    require_once $this->root . '/core/includes/update.inc';

    drupal_load_updates();
    update_fix_compatibility();

    if ($request->query->get('continue')) {
      $_SESSION['update_ignore_warnings'] = TRUE;
+165 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
use Drupal\Core\Extension\Extension;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\path_alias\Entity\PathAlias;
use Drupal\path_alias\PathAliasStorage;
use Drupal\Core\Site\Settings;
@@ -33,6 +34,9 @@
 */
function system_requirements($phase) {
  global $install_state;
  // Reset the extension lists.
  \Drupal::service('extension.list.module')->reset();
  \Drupal::service('extension.list.theme')->reset();
  $requirements = [];

  // Report Drupal version
@@ -856,28 +860,71 @@ function system_requirements($phase) {
  }

  // Display an error if a newly introduced dependency in a module is not resolved.
  if ($phase == 'update') {
  if ($phase === 'update' || $phase === 'runtime') {
    $create_extension_incompatibility_list = function ($extension_names, $description, $title) {
      // Use an inline twig template to:
      // - Concatenate two MarkupInterface objects and preserve safeness.
      // - Use the item_list theme for the extension list.
      $template = [
        '#type' => 'inline_template',
        '#template' => '{{ description }}{{ extensions }}',
        '#context' => [
          'extensions' => [
            '#theme' => 'item_list',
          ],
        ],
      ];
      $template['#context']['extensions']['#items'] = $extension_names;
      $template['#context']['description'] = $description;
      return [
        'title' => $title,
        'value' => [
          'list' => $template,
          'handbook_link' => [
            '#markup' => t(
              'Review the <a href=":url"> suggestions for resolving this incompatibility</a> to repair your installation, and then re-run update.php.',
              [':url' => 'https://www.drupal.org/docs/8/update/troubleshooting-database-updates']
            ),
          ],
        ],
        'severity' => REQUIREMENT_ERROR,
      ];
    };
    $profile = \Drupal::installProfile();
    $files = \Drupal::service('extension.list.module')->getList();
    foreach ($files as $module => $file) {
      // Ignore disabled modules and installation profiles.
      if (!$file->status || $module == $profile) {
    $files += \Drupal::service('extension.list.theme')->getList();
    $core_incompatible_extensions = [];
    $php_incompatible_extensions = [];
    foreach ($files as $extension_name => $file) {
      // Ignore uninstalled extensions and installation profiles.
      if (!$file->status || $extension_name == $profile) {
        continue;
      }
      // Check the module's PHP version.

      $name = $file->info['name'];
      if (!empty($file->info['core_incompatible'])) {
        $core_incompatible_extensions[$file->info['type']][] = $name;
      }

      // Check the extension's PHP version.
      $php = $file->info['php'];
      if (version_compare($php, PHP_VERSION, '>')) {
        $requirements['php']['description'] .= t('@name requires at least PHP @version.', ['@name' => $name, '@version' => $php]);
        $requirements['php']['severity'] = REQUIREMENT_ERROR;
        $php_incompatible_extensions[$file->info['type']][] = $name;
      }

      // @todo Remove this 'if' block to allow checking requirements of themes
      //   https://www.drupal.org/project/drupal/issues/474684.
      if ($file->info['type'] !== 'module') {
        continue;
      }

      // Check the module's required modules.
      /** @var \Drupal\Core\Extension\Dependency $requirement */
      foreach ($file->requires as $requirement) {
        $required_module = $requirement->getName();
        // Check if the module exists.
        if (!isset($files[$required_module])) {
          $requirements["$module-$required_module"] = [
          $requirements["$extension_name-$required_module"] = [
            'title' => t('Unresolved dependency'),
            'description' => t('@name requires this module.', ['@name' => $name]),
            'value' => t('@required_name (Missing)', ['@required_name' => $required_module]),
@@ -890,7 +937,7 @@ function system_requirements($phase) {
        $required_name = $required_file->info['name'];
        $version = str_replace(\Drupal::CORE_COMPATIBILITY . '-', '', $required_file->info['version']);
        if (!$requirement->isCompatible($version)) {
          $requirements["$module-$required_module"] = [
          $requirements["$extension_name-$required_module"] = [
            'title' => t('Unresolved dependency'),
            'description' => t('@name requires this module and version. Currently using @required_name version @version', ['@name' => $name, '@required_name' => $required_name, '@version' => $version]),
            'value' => t('@required_name (Version @compatibility required)', ['@required_name' => $required_name, '@compatibility' => $requirement->getConstraintString()]),
@@ -900,6 +947,115 @@ function system_requirements($phase) {
        }
      }
    }
    if (!empty($core_incompatible_extensions['module'])) {
      $requirements['module_core_incompatible'] = $create_extension_incompatibility_list(
        $core_incompatible_extensions['module'],
        new PluralTranslatableMarkup(
          count($core_incompatible_extensions['module']),
        'The following module is installed, but it is incompatible with Drupal @version:',
        'The following modules are installed, but they are incompatible with Drupal @version:',
        ['@version' => \Drupal::VERSION]
        ),
        new PluralTranslatableMarkup(
          count($core_incompatible_extensions['module']),
          'Incompatible module',
          'Incompatible modules'
        )
      );
    }
    if (!empty($core_incompatible_extensions['theme'])) {
      $requirements['theme_core_incompatible'] = $create_extension_incompatibility_list(
        $core_incompatible_extensions['theme'],
        new PluralTranslatableMarkup(
          count($core_incompatible_extensions['theme']),
          'The following theme is installed, but it is incompatible with Drupal @version:',
          'The following themes are installed, but they are incompatible with Drupal @version:',
          ['@version' => \Drupal::VERSION]
        ),
        new PluralTranslatableMarkup(
          count($core_incompatible_extensions['theme']),
          'Incompatible theme',
          'Incompatible themes'
        )
      );
    }
    if (!empty($php_incompatible_extensions['module'])) {
      $requirements['module_php_incompatible'] = $create_extension_incompatibility_list(
        $php_incompatible_extensions['module'],
        new PluralTranslatableMarkup(
          count($php_incompatible_extensions['module']),
          'The following module is installed, but it is incompatible with PHP @version:',
          'The following modules are installed, but they are incompatible with PHP @version:',
          ['@version' => phpversion()]
        ),
        new PluralTranslatableMarkup(
          count($php_incompatible_extensions['module']),
          'Incompatible module',
          'Incompatible modules'
        )
      );
    }
    if (!empty($php_incompatible_extensions['theme'])) {
      $requirements['theme_php_incompatible'] = $create_extension_incompatibility_list(
        $php_incompatible_extensions['theme'],
        new PluralTranslatableMarkup(
          count($php_incompatible_extensions['theme']),
          'The following theme is installed, but it is incompatible with PHP @version:',
          'The following themes are installed, but they are incompatible with PHP @version:',
          ['@version' => phpversion()]
        ),
        new PluralTranslatableMarkup(
          count($php_incompatible_extensions['theme']),
          'Incompatible theme',
          'Incompatible themes'
        )
      );
    }

    // Look for invalid modules.
    $extension_config = \Drupal::configFactory()->get('core.extension');
    /** @var \Drupal\Core\Extension\ExtensionList $extension_list */
    $extension_list = \Drupal::service('extension.list.module');
    $is_missing_extension = function ($extension_name) use (&$extension_list) {
      return !$extension_list->exists($extension_name);
    };

    $invalid_modules = array_filter(array_keys($extension_config->get('module')), $is_missing_extension);

    if (!empty($invalid_modules)) {
      $requirements['invalid_module'] = $create_extension_incompatibility_list(
        $invalid_modules,
        new PluralTranslatableMarkup(
          count($invalid_modules),
          'The following module is marked as installed in the core.extension configuration, but it is missing:',
          'The following modules are marked as installed in the core.extension configuration, but they are missing:'
        ),
        new PluralTranslatableMarkup(
          count($invalid_modules),
          'Missing or invalid module',
          'Missing or invalid modules'
        )
      );
    }

    // Look for invalid themes.
    $extension_list = \Drupal::service('extension.list.theme');
    $invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_extension);
    if (!empty($invalid_themes)) {
      $requirements['invalid_theme'] = $create_extension_incompatibility_list(
        $invalid_themes,
        new PluralTranslatableMarkup(
          count($invalid_themes),
          'The following theme is marked as installed in the core.extension configuration, but it is missing:',
          'The following themes are marked as installed in the core.extension configuration, but they are missing:'
        ),
        new PluralTranslatableMarkup(
          count($invalid_themes),
          'Missing or invalid theme',
          'Missing or invalid themes'
        )
      );
    }
  }

  // Returns Unicode library status and errors.
Loading