5

When a user calls the front page of our site we want to check the country from which the user is surfing and redirect the user directly to the country specific front page.

We chose to use the hook_boot() callback to implement this.

Now we have the problem that we want to save the state "is_redirected" into a global variable that will still be available when the redirected page is loading and is calling our hook_boot() again in order to prevent redirect loops.

We thought about using session management, so we loaded the drupal session code with

include_once(DRUPAL_ROOT . '/includes/session.inc');

The question is now, what is the next step after loading session.inc, do we have to call "drupal_session_initialize()", add a custom entry into the superglobal $_SESSION variable and this custom entry + our session will be available after the redirect?

Thank you in advance!

  • What version of Drupal? – mpdonadio Jul 13 '12 at 13:08
  • Drupal version 7.14 – Matthias Jul 13 '12 at 14:43
  • Why can't you pass is_redirected as a URL parameter, as opposed to using a session? Just curious. – amateur barista Aug 16 '12 at 15:55
  • We solved it in another way: because we're doing the redirect with drupal_goto() and the session gets lost when we call drupal_goto() we solved it by checking the language of the requested page. If its the front page and if it is not the correct one according to geoip we do a redirect with drupal_goto(). If the requested language page and geiop concur we do nothing. So the main task was finding out whether we have a front page to load and what language version of the front page is it. – Matthias Aug 16 '12 at 17:11
4

hook_boot() is invoked too early to use the session, as Drupal has not yet initialised the session. Furthermore, hook_boot() has been removed from Drupal 8. hook_init() is not invoked for cached pages, but there is a trick to avoid the page is cached: Call drupal_page_is_cacheable(FALSE) in hook_init().

The function that actually stores the current page in cache is drupal_page_set_cache(), which contains the following code.

  if (drupal_page_is_cacheable()) {
    $cache = (object) array(
      'cid' => $base_root . request_uri(), 
      'data' => array(
        'path' => $_GET['q'], 
        'body' => ob_get_clean(), 
        'title' => drupal_get_title(), 
        'headers' => array(),
      ), 
      'expire' => CACHE_TEMPORARY, 
      'created' => REQUEST_TIME,
    );

    // Restore preferred header names based on the lower-case names returned
    // by drupal_get_http_header().
    $header_names = _drupal_set_preferred_header_name();
    foreach (drupal_get_http_header() as $name_lower => $value) {
      $cache->data['headers'][$header_names[$name_lower]] = $value;
      if ($name_lower == 'expires') {
        // Use the actual timestamp from an Expires header if available.
        $cache->expire = strtotime($value);
      }
    }

    if ($cache->data['body']) {
      if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
        $cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP);
      }
      cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire);
    }
    return $cache;
  }

When drupal_page_is_cacheable() returns FALSE, the page is not cached; drupal_page_is_cacheable(), when called without arguments, returns the value of the parameter passed to the function in a previous call.

As hook_init() is invoked before drupal_page_set_cache(), calling drupal_page_is_cacheable(FALSE) causes the current page not to be cached.

I would call drupal_page_is_cacheable(FALSE) strictly when necessary, which in you case means when you are sure you need to set $_SESSION['is_redirected'].

Notice that a page is served from the cache when there isn't any session enabled. The function called to get the page from cache (_drupal_bootstrap_page_cache()) contains the following code.

  // 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) {
    // …
  }

If then you need to clear the cache for the front page, you can use the following code.

cache_clear_all(url('<front>', array('absolute' => TRUE)),  'cache_page');

The other alternative to hook_boot(), and hook_init() I can think of is hook_exit() which is called also for cached pages. The only reason I would not use it is that it is invoked after the current page is rendered, which means that when you redirect the user to a different page, the user would wait for two different pages to be rendered (the one rendered before the redirect, and the one to which the user is redirected).

If you are going to use hook_exit(), you need at least to call drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); if then you need specific functions, you need to change bootstrap phase, such as with drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) when you need a function defined from another module.

Remember that with hook_boot() you cannot call functions defined from other modules, as they are not yet loaded; you can only use PHP functions, and a very strict number of Drupal functions, which at least includes the functions defined in bootstrap.inc.

0

I have implemented same functionality in one of my sites using init hook instead.

function hook_init(){
 if(drupal_is_front_page()){
  $ip = ip_address();
  $cc = geoip_country_code($ip); // From GeoIP Module
  // Now check $cc with countries supported by the site
  if(array_search($cc, $country_specific_sites){
    // $country_specific_sites is an array of countries supported by the site
    // redirect !
  }
 }
}

To avoid infinite redirection all conditions is wrapped in drupal_is_front_page(). Alternatively you can declare this as a different function and call it from page-front.tpl.php

  • 2
    Does this work when you have caching turned on? – mpdonadio Jul 13 '12 at 13:20
  • Yeah it does work – GoodSp33d Jul 13 '12 at 13:22
  • 2
    It shouldn't...hook_init() isn't called for cached pages – Clive Jul 13 '12 at 13:25
  • Oops ! I believe i had used this module drupal.org/project/cacheexclude – GoodSp33d Jul 13 '12 at 13:28
  • 2
    Exactly, we have to solve this before caching, that's why we're using hook_boot(). drupal_is_front_page() is no option for us because we have a multilingual site, with a front page defined for every language. drupal_is_front_page() essentially just does a variable_get('site_frontpage', 'node') which will always return the default language front page. – Matthias Jul 13 '12 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.