In settings.php you can make various changes to the $conf[] array which are lost. The domains module does a custom bootstrap with this code:

sites/all/modules/contrib/domain.bootstrap.inc:45 in domain_bootstrap()

  if (!isset($GLOBALS['base_root'])) {
    drupal_settings_initialize();
  }

includes/bootstrap.inc:550 in drupal_settings_initialize()

  global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
  $conf = array();

  if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
    include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
  }

As the domain bootstrap is run at the bottom of settings.php the second time this function is run the settings.php will not be included. This resets the $conf to a blank array limiting configuration options in settings.php.

Comments

R.Muilwijk’s picture

StatusFileSize
new611 bytes
R.Muilwijk’s picture

After talking on different medium it was proposed to move the $conf changes after the domain include in settings.php. I do not think this is the solution, atleast the install.txt file should be changed if so but we also have to deal with this:

1) When domain bootstrap is started only phase DRUPAL_BOOTSTRAP_CONFIGURATION has been done.

2) In Domain bootstrap the drupal bootstrap is started to run till DRUPAL_BOOTSTRAP_DATABASE with a blank $conf[]. The problem is that in the DRUPAL_BOOTSTRAP_PAGE_CACHE phase variables are already used which can't be overridden in settings.php but do not offer an interface in drupal:

Drupal bootstrap phase page cache:

  foreach (variable_get('cache_backends', array()) as $include) {
    require_once DRUPAL_ROOT . '/' . $include;
  }
  // Check for a cache mode force from settings.php.
  if (variable_get('page_cache_without_database')) {
    $cache_enabled = TRUE;
  }
  .......
      if (variable_get('page_cache_invoke_hooks', TRUE)) {
        bootstrap_invoke_all('exit');
      }
  ........
agentrickard’s picture

Right. Some notes.

1) Twitter is not a support channel. I check the queue every business day, and you'd have gotten a better response without the tweet.

2) This change was a hot bugfix to correct #1046844: domain_bootstrap breaks page cache.

3) The check for 'base_root' should indicate that $conf has not been instatiated yet unless it was done manually in settings.php.

4) Normally, people have to put $conf below the include in these cases because Domain Conf can reset hardcoded $conf values (which is common when using things like i18n). If this is the case here, we need a documentation fix.

5) All that said, I suspect the patch may solve the issue. (Though it will not stop Domain Conf from changing variables set in this manner.)

What are the steps to replicate the original problem? Did you put vars in settings.php?

R.Muilwijk’s picture

1) Ok

2) Ok

3) Well the flow is like this.. the first configuration phase is called and therefor drupal_settings_initialize(). However it includes the settings.php without setting the base_root yet. So the domain_bootstrap is actually run inside the first call of the drupal_settings_initialize(). After finishing the bootstrap it continues and finally sets the base_root (which is also set by domain_bootstrap).

inside drupal_settings_initialize():

function drupal_settings_initialize() {
  global $base_url, $base_path, $base_root;

  // Export the following settings.php variables to the global namespace
  global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
  $conf = array();

  if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
    include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
  }

4) That is a solution, settings.php however comes with a default format and already has $conf variables in it (commented).

5) I do think this patch will fix it. Reproduction steps is for example installing the apc cache backend http://drupal.org/project/apc

agentrickard’s picture

StatusFileSize
new915 bytes

Got it. This is actually a great bug report.

This was caused by the hot fix, and if we change it, there will still be cases where #4 is a problem (but only for people using Domain Conf).

I was hoping that there is some better way to set 'base_root', without just hacking some core code into the bootstrap phase.

Does this patch also fix the issue? It may be safer, since it doesn't try to run the initialization routine.

The only issue I see with this version is you can't set $base_url in settings.php (which you can never do with DA anyway.)

R.Muilwijk’s picture

I think we need to have a closer look to this.. by doing this change the flow is like this:

1 ) Inside DRUPAL_BOOTSTRAP_CONFIGURATION the Domain bootstrap is run inside drupal_settings_initialize() before $base_url, $base_path, $base_root, $cookie_domain and session_name() are set.

  if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
    include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
  }
  ......
  $base_path = ...;
  $base_url = ....;
  $base_root = ....;
  $cookie_domain = .....;
  session_name(.....);

2) Domain bootstrap provides the $base_root with copied functionality from core.

-  // Ensure that core globals are loaded.
+  // Ensure that 'base_root' is loaded to allow caching.
   if (!isset($GLOBALS['base_root'])) {
-    drupal_settings_initialize();
+    $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
+    $GLOBALS['base_root'] = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
   }

3) Domain Bootstrap starts to boot Drupal untill the database skipping the first phase (DRUPAL_BOOTSTRAP_CONFIGURATION):

    while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) {
      $current_phase = array_shift($phases);

      // This function is re-entrant. Only update the completed phase when the
      // current call actually resulted in a progress in the bootstrap process.
      if ($current_phase > $stored_phase) {
        $stored_phase = $current_phase;
      }

4 ) Because of booting till database the DRUPAL_BOOTSTRAP_PAGE_CACHE phase is run too. This is difficult because it uses session_name(). I do not know or it gives actual problems but from a code view it is possible to generate problems.

function _drupal_bootstrap_page_cache() {
  ......
  // If there is no session cookie and cache is enabled (or forced), try
  // to serve a cached page.
  if (!isset($_COOKIE[session_name()]) && $cache_enabled) {
    // Make sure there is a user object because its timestamp will be
    // checked, hook_boot might check for anonymous user etc.

I think the question is why try to set $base_root? When I have a look at the domain code it doesn't use the $base_root:

`grep -r "base_root" *`:
domain.bootstrap.inc:  if (!isset($GLOBALS['base_root'])) {

Instead the following two functions resolve this:

domain.module:2364
function domain_resolve_host($name = '') {
  if (empty($name)) {
    $name = domain_request_name();
  }
  return domain_lookup_simple($name);
}

domain:2383
function domain_request_name() {
  if (!empty($_SERVER['HTTP_HOST'])) {
    // We lower case this, since EXAMPLE.com == example.com.
    return strtolower(rtrim($_SERVER['HTTP_HOST']));
  }
  else {
    $domain = domain_default(FALSE, FALSE);
    return $domain['subdomain'];
  }
}

As I think of it.. we'd proberly have to set the correct session_name() for the cache to stay working. What is the best approach because it will involve making the drupal_settings_initialize() to run before the DRUPAL_BOOTSTRAP_PAGE_CACHE phase.

agentrickard’s picture

If we don't set $base_root, page cache fails, because the cache id doesn't contain the proper URL. See #1046844: domain_bootstrap breaks page cache and http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/drupal....

There were changes in Drupal bootstrap from d6 to d7 that required this change. So we need to capture what elements must be present before we can act (which your last comment does quite well.)

In D6, doing our operations in hook_boot() was too late. For instance, we might want to allow page caching on example.com but not on one.example.com.

In D6, we could do that. We need to be able to do that here, too.

So maybe we do initialize vars twice, but prevent overwrites, as in your first patch.

agentrickard’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new2.06 KB

I took a hard look at this, and I think you're approach is the proper one. We end up calling drupal_settings_initialize() from within that function (!), but thanks to include_once calls in that function, we don't run into a recursive loop.

Here's a patch that adds documentation for clarity.

agentrickard’s picture

All tests pass on HEAD, so this looks ready to go.

Any final comments?

R.Muilwijk’s picture

Seems right to me!

agentrickard’s picture

Status: Reviewed & tested by the community » Fixed

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

Pedro Lozano’s picture

Status: Closed (fixed) » Active

This problem was introduced again in commit 9754e6907.

Was the patch accidentally removed on #1189916: Login rejected on cached pages ? Or intentionally for some reason?

Pedro Lozano’s picture

Priority: Major » Minor

It seems the $conf['whatever'] = 'value'; lines in my settings.inc need to be BEFORE including the domain/settings.inc file (call to domain_bootstrap()).

If the $conf lines are placed after the call to domain_bootstrap() then they still get lost, odd behavior.

Should this be documented/fixed or is that way by design?

agentrickard’s picture

Status: Active » Closed (works as designed)

It is documented that way. Has been for years.

Probably worth a quick note explicitly about $conf on the "Add settings.inc" heading here -- https://drupal.org/node/1096962.

Please update.