function update_prepare_d8_bootstrap

Performs extra steps required to bootstrap when using a Drupal 7 database.

Users who still have a Drupal 7 database (and are in the process of updating to Drupal 8) need extra help before a full bootstrap can be achieved. This function does the necessary preliminary work that allows the bootstrap to be successful.

No access check has been performed when this function is called, so no irreversible changes to the database are made here.

1 call to update_prepare_d8_bootstrap()
update.php in drupal/core/update.php
Administrative page for handling updates from one Drupal version to another.

File

drupal/core/includes/update.inc, line 93
Drupal database update API.

Code

function update_prepare_d8_bootstrap() {
  include_once __DIR__ . '/install.inc';
  include_once __DIR__ . '/schema.inc';

  // Bootstrap to configuration.
  drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);

  // During the bootstrap to DRUPAL_BOOTSTRAP_PAGE_CACHE, code will try to read
  // the cache but the cache tables are not compatible yet. Use the Null backend
  // by default to avoid exceptions.
  $settings = settings()
    ->getAll();
  $settings['cache']['default'] = 'cache.backend.memory';
  new Settings($settings);

  // Enable UpdateBundle service overrides. While the container_bundles array
  // does not need a key, let's use so it can be removed once the upgrade are
  // finished. @see update_flush_all_caches()
  $GLOBALS['conf']['container_bundles']['UpdateBundle'] = 'Drupal\\Core\\DependencyInjection\\UpdateBundle';

  // Check whether settings.php needs to be rewritten.
  $settings_exist = !empty($GLOBALS['config_directories']);
  if (!$settings_exist || !is_dir(config_get_config_directory('active')) || !is_dir(config_get_config_directory('staging'))) {
    drupal_install_config_directories();
  }

  // Bootstrap the kernel.
  // Do not attempt to dump and write it.
  $kernel = new DrupalKernel('update', FALSE, drupal_classloader(), FALSE);
  $kernel
    ->boot();

  // If any of the required settings needs to be written, then settings.php
  // needs to be writable.
  if (!$settings_exist) {
    $settings_file = conf_path() . '/settings.php';
    $writable = drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_WRITABLE);
    $requirements['settings file']['title'] = 'Settings file';
    if ($writable) {
      $requirements['settings file'] += array(
        'value' => 'settings.php is writable.',
      );
    }
    else {
      $requirements['settings file'] += array(
        'value' => 'settings.php is not writable.',
        'severity' => REQUIREMENT_ERROR,
        'description' => 'Drupal requires write permissions to <em>' . $settings_file . '</em> during the update process. If you are unsure how to grant file permissions, consult the <a href="http://drupal.org/server-permissions">online handbook</a>.',
      );
    }
    update_extra_requirements($requirements);
  }

  // Bootstrap the database.
  drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);

  // If the site has not updated to Drupal 8 yet, check to make sure that it is
  // running an up-to-date version of Drupal 7 before proceeding. Note this has
  // to happen AFTER the database bootstraps because of
  // drupal_get_installed_schema_version().
  try {
    $system_schema = drupal_get_installed_schema_version('system');
  } catch (\Exception $e) {
    $system_schema = db_query('SELECT schema_version FROM {system} WHERE name = :system', array(
      ':system' => 'system',
    ))
      ->fetchField();
  }
  if ($system_schema < 8000) {
    $has_required_schema = $system_schema >= REQUIRED_D7_SCHEMA_VERSION;
    $requirements = array(
      'drupal 7 version' => array(
        'title' => 'Drupal 7 version',
        'value' => $has_required_schema ? 'You are running a current version of Drupal 7.' : 'You are not running a current version of Drupal 7',
        'severity' => $has_required_schema ? NULL : REQUIREMENT_ERROR,
        'description' => $has_required_schema ? '' : 'Please update your Drupal 7 installation to the most recent version before attempting to upgrade to Drupal 8',
      ),
    );
    update_extra_requirements($requirements);

    // @todo update.php stages seem to be completely screwed up; the initial
    //   requirements check is not supposed to change the system. All of the
    //   following code seems to have been mistakenly/unknowingly added here and
    //   does not belong into update_prepare_d8_bootstrap().
    if ($has_required_schema) {
      if (!db_table_exists('key_value')) {
        $specs = array(
          'description' => 'Generic key-value storage table. See the state system for an example.',
          'fields' => array(
            'collection' => array(
              'description' => 'A named collection of key and value pairs.',
              'type' => 'varchar',
              'length' => 128,
              'not null' => TRUE,
              'default' => '',
            ),
            'name' => array(
              'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.',
              'type' => 'varchar',
              'length' => 128,
              'not null' => TRUE,
              'default' => '',
            ),
            'value' => array(
              'description' => 'The value.',
              'type' => 'blob',
              'not null' => TRUE,
              'size' => 'big',
              'translatable' => TRUE,
            ),
          ),
          'primary key' => array(
            'collection',
            'name',
          ),
        );
        db_create_table('key_value', $specs);
      }
      if (!db_table_exists('cache_tags')) {
        $table = array(
          'description' => 'Cache table for tracking cache tags related to the cache bin.',
          'fields' => array(
            'tag' => array(
              'description' => 'Namespace-prefixed tag string.',
              'type' => 'varchar',
              'length' => 255,
              'not null' => TRUE,
              'default' => '',
            ),
            'invalidations' => array(
              'description' => 'Number incremented when the tag is invalidated.',
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
            ),
            'deletions' => array(
              'description' => 'Number incremented when the tag is deleted.',
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
            ),
          ),
          'primary key' => array(
            'tag',
          ),
        );
        db_create_table('cache_tags', $table);
      }
      if (!db_table_exists('cache_config')) {
        $spec = array(
          'description' => 'Cache table for configuration data.',
          'fields' => array(
            'cid' => array(
              'description' => 'Primary Key: Unique cache ID.',
              'type' => 'varchar',
              'length' => 255,
              'not null' => TRUE,
              'default' => '',
            ),
            'data' => array(
              'description' => 'A collection of data to cache.',
              'type' => 'blob',
              'not null' => FALSE,
              'size' => 'big',
            ),
            'expire' => array(
              'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
            ),
            'created' => array(
              'description' => 'A Unix timestamp indicating when the cache entry was created.',
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
            ),
            'serialized' => array(
              'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
              'type' => 'int',
              'size' => 'small',
              'not null' => TRUE,
              'default' => 0,
            ),
            'tags' => array(
              'description' => 'Space-separated list of cache tags for this entry.',
              'type' => 'text',
              'size' => 'big',
              'not null' => FALSE,
            ),
            'checksum_invalidations' => array(
              'description' => 'The tag invalidation sum when this entry was saved.',
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
            ),
            'checksum_deletions' => array(
              'description' => 'The tag deletion sum when this entry was saved.',
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
            ),
          ),
          'indexes' => array(
            'expire' => array(
              'expire',
            ),
          ),
          'primary key' => array(
            'cid',
          ),
        );
        db_create_table('cache_config', $spec);
      }
      require_once DRUPAL_ROOT . '/core/modules/system/system.install';
      $tables = array(
        'cache',
        'cache_bootstrap',
        'cache_block',
        'cache_field',
        'cache_filter',
        'cache_form',
        'cache_image',
        'cache_menu',
        'cache_page',
        'cache_path',
        'cache_update',
      );
      foreach ($tables as $table) {
        update_add_cache_columns($table);
      }

      // Bootstrap variables so we can update theme while preparing the update
      // process.
      drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);

      // Update the 'language_default' system variable, if configured.
      // Required to run before drupal_install_config_directories(), since that
      // triggers a call into system_stream_wrappers(), which calls t(), which
      // calls into language_default().
      $language_default = variable_get('language_default');
      if (!empty($language_default) && (isset($language_default->langcode) || isset($language_default->language))) {
        if (!isset($language_default->langcode)) {
          $language_default->langcode = $language_default->language;
        }
        unset($language_default->language);

        // In D8, the 'language_default' is not anymore an object, but an array,
        // so make sure that the new value that is saved into this variable is an
        // array.
        variable_set('language_default', (array) $language_default);
      }
      $module_config = config('system.module');
      $disabled_modules = config('system.module.disabled');
      $theme_config = config('system.theme');
      $disabled_themes = config('system.theme.disabled');
      $schema_store = Drupal::keyValue('system.schema');

      // Load system.module, because update_prepare_d8_bootstrap() is called in
      // the initial minimal update.php bootstrap that performs the core
      // requirements check.
      require_once DRUPAL_ROOT . '/core/modules/system/system.module';

      // Make sure that the bootstrap cache is cleared as that might contain
      // incompatible data structures.
      cache('bootstrap')
        ->deleteAll();

      // Retrieve all installed extensions from the {system} table.
      // Uninstalled extensions are ignored and not converted.
      $result = db_query('SELECT name, status, weight, schema_version, type FROM {system} WHERE type = :theme OR (type = :module AND schema_version <> :schema_uninstalled)', array(
        ':theme' => 'theme',
        ':module' => 'module',
        ':schema_uninstalled' => SCHEMA_UNINSTALLED,
      ));
      $module_data = _system_rebuild_module_data();

      // Migrate each extension into configuration, varying by the extension's
      // status, and record its schema version.
      foreach ($result as $record) {
        if ($record->type == 'module') {
          if ($record->status && isset($module_data[$record->name])) {
            $module_config
              ->set('enabled.' . $record->name, $record->weight);
          }
          else {
            $disabled_modules
              ->set($record->name, $record->weight);
          }
        }
        elseif ($record->type == 'theme') {
          if ($record->status) {
            $theme_config
              ->set('enabled.' . $record->name, 0);
          }
          else {
            $disabled_themes
              ->set($record->name, 0);
          }
        }
        $schema_store
          ->set($record->name, $record->schema_version);
      }
      $sorted_modules = module_config_sort($module_config
        ->get('enabled'));
      $module_config
        ->set('enabled', $sorted_modules)
        ->save();
      $sorted_with_filenames = array();
      foreach (array_keys($sorted_modules) as $m) {
        $sorted_with_filenames[$m] = drupal_get_filename('module', $m);
      }
      Drupal::moduleHandler()
        ->setModuleList($sorted_with_filenames);
      $disabled_modules
        ->save();
      $theme_config
        ->save();
      $disabled_themes
        ->save();

      // Migrate the private key to state. This is used to create the token for
      // the upgrade batch so needs to be be done before the upgrade has begun.
      update_variables_to_state(array(
        'drupal_private_key' => 'system.private_key',
      ));

      // Update the dynamic include paths that might be used before running the
      // proper update functions.
      update_prepare_stored_includes();

      // Update the environment for the language bootstrap if needed.
      update_prepare_d8_language();

      // Rebuild kernel after new language fields are added in the database
      // because the translation service depends on them being there.
      Drupal::service('kernel')
        ->updateModules($sorted_with_filenames, $sorted_with_filenames);

      // Change language column to langcode in url_alias.
      if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) {
        db_drop_index('url_alias', 'alias_language_pid');
        db_drop_index('url_alias', 'source_language_pid');
        $langcode_spec = array(
          'description' => "The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.",
          'type' => 'varchar',
          'length' => 12,
          'not null' => TRUE,
          'default' => '',
        );
        $langcode_indexes = array(
          'indexes' => array(
            'alias_langcode_pid' => array(
              'alias',
              'langcode',
              'pid',
            ),
            'source_langcode_pid' => array(
              'source',
              'langcode',
              'pid',
            ),
          ),
        );
        db_change_field('url_alias', 'language', 'langcode', $langcode_spec, $langcode_indexes);
      }
    }
  }

  // Now remove the cache override.
  $settings = settings()
    ->getAll();
  unset($settings['cache']['default']);
  new Settings($settings);
  $kernel = new DrupalKernel('update', FALSE, drupal_classloader(), FALSE);
  $kernel
    ->boot();
}