You are here

domain.bootstrap.inc in Domain Access 7.2

Same filename and directory in other branches
  1. 6.2 domain.bootstrap.inc
  2. 7.3 domain.bootstrap.inc

Domain bootstrap file.

The domain bootstrap process is initiated in domain/settings.inc which is (supposed to be) included in the user's settings.php and therefore initiated during drupal's first bootstrap phase (DRUPAL_BOOTSTRAP_CONFIGURATION).

The purpose of this is to allow domain-based modules to modify basic drupal systems like the variable or database/prefix system - before they are used by any other modules.

File

domain.bootstrap.inc
View source
<?php

/**
 * @file
 * Domain bootstrap file.
 *
 * The domain bootstrap process is initiated in domain/settings.inc which is
 * (supposed to be) included in the user's settings.php and therefore initiated
 * during drupal's first bootstrap phase (DRUPAL_BOOTSTRAP_CONFIGURATION).
 *
 * The purpose of this is to allow domain-based modules to modify basic drupal
 * systems like the variable or database/prefix system - before they are used by
 * any other modules.
 *
 * @ingroup domain
 */

/**
 * Domain bootstrap phase 1: makes sure that database is initialized and
 * loads all necessary module files.
 */
define('DOMAIN_BOOTSTRAP_INIT', 0);

/**
 * Domain bootstrap phase 2: resolves host and does a lookup in the {domain}
 * table. Also invokes hook "hook_domain_bootstrap_lookup".
 */
define('DOMAIN_BOOTSTRAP_NAME_RESOLVE', 1);

/**
 * Domain bootstrap phase 3: invokes hook_domain_bootstrap_full().
 */
define('DOMAIN_BOOTSTRAP_FULL', 2);

/**
 * Domain module bootstrap: calls all bootstrap phases.
 *
 * Effectively, we add our own bootstrap phase to Drupal core.
 * This allows us to do selective configuration changes before Drupal
 * has a chance to react. For example, the Domain Conf module allows
 * page caching to be set to "on" for example.com but "off" for
 * no-cache.example.com.
 *
 * The big issues here are command-line interactions, which are slightly
 * different; and the fact that drupal_settings_initialize() is needed to
 * properly hit the page cache.
 *
 * The initial check for GLOBALS['base_root'] lets us know that we have
 * already run the normal configuration routine and are now free to alter
 * settings per-domain.
 *
 * Note that this problem is largely caused by the need to jump ahead
 * in drupal_bootstrap() in order to have database functions available.
 */
function domain_bootstrap() {
  global $_domain;

  // Initialize our global variable.
  $_domain = array();

  // Ensure that core globals are loaded so that we don't break page caching.
  // See http://drupal.org/node/1046844 and http://drupal.org/node/1189916.
  if (!isset($GLOBALS['base_root'])) {
    domain_settings_initialize();
  }
  $phases = array(
    DOMAIN_BOOTSTRAP_INIT,
    DOMAIN_BOOTSTRAP_NAME_RESOLVE,
    DOMAIN_BOOTSTRAP_FULL,
  );
  foreach ($phases as $phase) {

    // We return NULL if the domain module is not enabled.  This prevents
    // load errors during module enable / disable.
    $error = _domain_bootstrap($phase);
    if ($error === NULL) {
      $_domain['error'] = -1;
      break;
    }
    elseif ($error === FALSE) {

      // watchdog() is not available here, and there may be other logging.
      // So we have to write an error message global variable and pick them up
      // in settings,inc.
      $_domain['error'] = $phase;
      break;
    }
  }
}

/**
 * Calls individual bootstrap phases.
 *
 * @param $phase
 *   The domain bootstrap phase to call.
 * @return
 *   Returns TRUE if the bootstrap phase was successful and FALSE otherwise.
 */
function _domain_bootstrap($phase) {
  global $_domain;
  switch ($phase) {
    case DOMAIN_BOOTSTRAP_INIT:

      // Make sure database is loaded.
      // The new update handler causes problems here, so we account for it.
      // Same with drush or other CLI resources. It turns out that the
      // drupal_is_cli() function is not consistent for php and drush scripts,
      // so instead, we check to see if the database driver has been loaded.
      // See http://drupal.org/node/1342740 for the latest background.
      $new_phase = FALSE;

      // If running from drush or update.php, we act differently.
      if (function_exists('drush_verify_cli') || function_exists('update_prepare_d7_bootstrap')) {
        $new_phase = TRUE;
      }
      drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE, $new_phase);

      // If the Domain Access module has been disabled, stop loading.
      $table = domain_get_primary_table('system');
      $check = (bool) db_query("SELECT status FROM {$table} WHERE name = 'domain' AND type = 'module'")
        ->fetchField();
      if (empty($check)) {
        return NULL;
      }

      // Load bootstrap modules registered by Domain Access.
      _domain_bootstrap_modules_load();

      // If essential core module file has not been loaded, bootstrap fails.
      if (!function_exists('domain_load')) {
        return FALSE;
      }
      break;
    case DOMAIN_BOOTSTRAP_NAME_RESOLVE:

      // Get the domain_id of the request.
      $_domain = domain_resolve_host();

      // If we don't have a valid domain id now, we can't really go on, bootstrap fails.
      if (empty($_domain) || !is_numeric($_domain['domain_id'])) {
        return FALSE;
      }
      break;
    case DOMAIN_BOOTSTRAP_FULL:

      // Grant access to all affiliates. See http://drupal.org/node/770650
      $_domain['site_grant'] = DOMAIN_SITE_GRANT;

      // Load all bootstrap processes registered with the module.
      _domain_bootstrap_invoke_all('full', $_domain);
      break;
  }
  return TRUE;
}

/**
 * Returns a list of modules which are loaded during domain_bootstrap phases.
 *
 * The domain module is always in the list of modules and has weight -99 so it
 * should usually be first one as well.
 *
 * @param $reset
 *   If set to TRUE the cached list of modules is updated with the value from
 *   the {variable} table again. Default value is FALSE. Optional.
 * @return
 *   An array of module names.
 */
function _domain_bootstrap_modules($reset = FALSE) {
  $modules =& drupal_static(__FUNCTION__, NULL);
  if ($reset || is_null($modules)) {
    $modules = _domain_bootstrap_modules_get();
    if (!is_array($modules)) {
      $modules = array();
    }
    if (!in_array('domain', $modules)) {
      $modules['-99'] = 'domain';
    }
    ksort($modules);
  }
  return $modules;
}

/**
 * Tries to load all domain bootstrap modules.
 * @see _domain_bootstrap_modules()
 */
function _domain_bootstrap_modules_load() {
  $modules = _domain_bootstrap_modules();
  foreach ($modules as $module) {
    drupal_load('module', $module);
  }
}

/**
 * Retrieves the value of the variable 'domain_bootstrap_modules' from the
 * {variable} table. This function does not use Drupal's variable system.
 *
 * @return
 *   An array containing module names.
 */
function _domain_bootstrap_modules_get() {
  global $conf;
  $key = 'domain_bootstrap_modules';
  $table = domain_get_primary_table('variable');
  $result = db_query("SELECT value FROM {$table} WHERE name = :name", array(
    ':name' => $key,
  ))
    ->fetchField();
  if (!empty($result)) {
    $conf[$key] = unserialize($result);
  }
  else {
    $conf[$key] = array(
      'domain',
    );
  }
  return $conf[$key];
}

/**
 * Tries to call specified hook on all domain_bootstrap modules.
 *
 * The hook function names are of the following format:
 * {$module}_domain_bootstrap_{$hook} where {$module}
 * is the name of the module implementing the hook and {$hook}
 * is the identifier for the concrete domain bootstrap hook.
 *
 * This function is basically a copy of module_invoke_all() adjusted to our
 * needs.
 *
 * @link http://api.drupal.org/api/function/module_invoke_all/6
 */
function _domain_bootstrap_invoke_all() {
  $args = func_get_args();
  $hook = $args[0];
  unset($args[0]);
  $return = array();
  foreach (_domain_bootstrap_modules() as $module) {
    $function = $module . '_domain_bootstrap_' . $hook;
    if (function_exists($function)) {
      $result = call_user_func_array($function, $args);
      if (isset($result) && is_array($result)) {
        $return = array_merge_recursive($return, $result);
      }
      elseif (isset($result)) {
        $return[] = $result;
      }
    }
  }
  return $return;
}

/**
 * Escape the names of the default tables for variable lookups.
 *
 * There are a few cases where we must directly query data from the
 * primary site's database. Due to table prefixing and Domain Prefix, we
 * must ensure that we are querying the correct table.
 *
 * When using this function, you should insert the $table result directly
 * into your query string, or use token replacement syntax. Do not
 * enclose the table name in brackets {}, as that defeats the purpose of
 * this function.
 *
 * @see _domain_conf_load_primary()
 *
 * @param $table
 *   The name of the base table to lookup.
 * @return
 *   A query-safe table name pointing to the primary domain and default
 *   database..
 */
function domain_get_primary_table($table) {
  global $databases;
  $db_prefix = isset($databases['default']['default']['prefix']) ? $databases['default']['default']['prefix'] : '';
  if (is_string($db_prefix)) {
    $table = $db_prefix . $table;
  }
  elseif (is_array($db_prefix)) {
    $table = $db_prefix['default'] . $table;
  }

  // db_escape_table() relies on a database connection, so we mirror that public
  // method here. See DatabaseConnection::escapeTable.
  return preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
}

/**
 * Replaces the logic from drupal_settings_initialize() so caching works.
 *
 * Problem: we cannot run drupal_settings_initialize() twice, so this logic
 * has to be cloned here, otherwise, user logins get corrupted. Without this
 * code, core page caching breaks because the url path isn't set properly
 * for use as a cache id.
 *
 * Further, calling drupal_settings_initialize() will reset $conf to an array
 * which destroys caching settings.
 *
 * @see drupal_settings_initialize()
 *
 * @link http://drupal.org/node/1046844
 * @link http://drupal.org/node/1189916
 */
function domain_settings_initialize() {
  global $base_url, $base_path, $base_root, $cookie_domain;
  $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
  if (isset($base_url)) {

    // Parse fixed base URL from settings.php.
    $parts = parse_url($base_url);
    $http_protocol = $parts['scheme'];
    if (!isset($parts['path'])) {
      $parts['path'] = '';
    }
    $base_path = $parts['path'] . '/';

    // Build $base_root (everything until first slash after "scheme://").
    $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
  }
  else {

    // Create base URL
    $http_protocol = $is_https ? 'https' : 'http';
    $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
    $base_url = $base_root;

    // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
    // be modified by a visitor.
    if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\\/')) {
      $base_path = $dir;
      $base_url .= $base_path;
      $base_path .= '/';
    }
    else {
      $base_path = '/';
    }
  }
  $base_secure_url = str_replace('http://', 'https://', $base_url);
  $base_insecure_url = str_replace('https://', 'http://', $base_url);
  if ($cookie_domain) {

    // If the user specifies the cookie domain, also use it for session name.
    $session_name = $cookie_domain;
  }
  else {

    // Otherwise use $base_url as session name, without the protocol
    // to use the same session identifiers across http and https.
    list(, $session_name) = explode('://', $base_url, 2);

    // HTTP_HOST can be modified by a visitor, but we already sanitized it
    // in drupal_settings_initialize().
    if (!empty($_SERVER['HTTP_HOST'])) {
      $cookie_domain = $_SERVER['HTTP_HOST'];

      // Strip leading periods, www., and port numbers from cookie domain.
      $cookie_domain = ltrim($cookie_domain, '.');
      if (strpos($cookie_domain, 'www.') === 0) {
        $cookie_domain = substr($cookie_domain, 4);
      }
      $cookie_domain = explode(':', $cookie_domain);
      $cookie_domain = '.' . $cookie_domain[0];
    }
  }

  // Per RFC 2109, cookie domains must contain at least one dot other than the
  // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
  if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
    ini_set('session.cookie_domain', $cookie_domain);
  }

  // To prevent session cookies from being hijacked, a user can configure the
  // SSL version of their website to only transfer session cookies via SSL by
  // using PHP's session.cookie_secure setting. The browser will then use two
  // separate session cookies for the HTTPS and HTTP versions of the site. So we
  // must use different session identifiers for HTTPS and HTTP to prevent a
  // cookie collision.
  if ($is_https) {
    ini_set('session.cookie_secure', TRUE);
  }

  // We have set $cookie_domain, so we must match $session_name to it, since
  // that's what will happen inside drupal_settings_initialize() after we run.
  // Essentially, we short-circuit the IF routine when this copied code runs
  // after our routine.
  $session_name = $cookie_domain;

  // Now set the session token correctly.
  $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS';
  session_name($prefix . substr(hash('sha256', $session_name), 0, 32));
}

Related topics

Functions

Namesort descending Description
domain_bootstrap Domain module bootstrap: calls all bootstrap phases.
domain_get_primary_table Escape the names of the default tables for variable lookups.
domain_settings_initialize Replaces the logic from drupal_settings_initialize() so caching works.
_domain_bootstrap Calls individual bootstrap phases.
_domain_bootstrap_invoke_all Tries to call specified hook on all domain_bootstrap modules.
_domain_bootstrap_modules Returns a list of modules which are loaded during domain_bootstrap phases.
_domain_bootstrap_modules_get Retrieves the value of the variable 'domain_bootstrap_modules' from the {variable} table. This function does not use Drupal's variable system.
_domain_bootstrap_modules_load Tries to load all domain bootstrap modules.

Constants

Namesort descending Description
DOMAIN_BOOTSTRAP_FULL Domain bootstrap phase 3: invokes hook_domain_bootstrap_full().
DOMAIN_BOOTSTRAP_INIT Domain bootstrap phase 1: makes sure that database is initialized and loads all necessary module files.
DOMAIN_BOOTSTRAP_NAME_RESOLVE Domain bootstrap phase 2: resolves host and does a lookup in the {domain} table. Also invokes hook "hook_domain_bootstrap_lookup".