Alternatives to hook_init() I use hook_init() to check the users' last access time. If the last access time is yesterday, I increment a counter and set some variables.

The problem is that hook_init() is sometimes executed more that once (i can see this using dsm()) for the same page load, so my code is executed multiple times resulting in wrong variables. 

Why is hook_init() executed more than once?
What would the best approach to my problem be? Should I use another hook?

I did some more digging on this:
I search for calls to the hook_init() (searched for string module_invoke_all('init');) but found only the core call). I dont know if this can be called differently.

This is my hook_init()

function episkeptis_achievements_init(){
    dsm('1st execution');
    dsm('REQUEST_TIME: '.format_date(REQUEST_TIME, 'custom', 'd/m/Y H:i:s').' ('.REQUEST_TIME.')');
}


and this is the output:

1st execution
REQUEST_TIME: 09/07/2012 11:20:32 (1341822032)


then, changed the dsm() message to dsm('2nd execution'); and executed again, this is the output:

1st execution
REQUEST_TIME: 09/07/2012 11:20:34 (1341822034)
2nd execution
REQUEST_TIME: 09/07/2012 11:22:28 (1341822148)


You can see that the code is executed twice. However, the first time executes an old copy of the code, and the second time the updated copy. There is also a 2 sec time difference.

This is a d7 version with php 5.3.10
hook_init() is invoked by Drupal only once for each requested page; it is the last step done in _drupal_bootstrap_full().

  // Drupal 6
  //
  // Let all modules take action before menu system handles the request
  // We do not want this while running update.php.
  if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
    module_invoke_all('init');
  }




  // Drupal 7
  //
  // Let all modules take action before the menu system handles the request.
  // We do not want this while running update.php.
  if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
    // Prior to invoking hook_init(), initialize the theme (potentially a custom
    // one for this page), so that:
    // - Modules with hook_init() implementations that call theme() or
//   theme_get_registry() don't initialize the incorrect theme.
    // - The theme can have hook_*_alter() implementations affect page building
//   (e.g., hook_form_alter(), hook_node_view_alter(), hook_page_alter()),
//   ahead of when rendering starts.
    menu_set_custom_theme();
    drupal_theme_initialize();
    module_invoke_all('init');
  }


If hook_init() is being executed more than once, you should discover why that happens. As far as I can see, none of the hook_init() implementations in Drupal checks it is being executed twice (see for example system_init(), or update_init()). If that is something that can normally happen with Drupal, then update_init() would first check if it has been already executed. 

If the counter is the number of consecutive days a user logged in, I would rather implements hook_init() with code similar to the following one.

// Drupal 7
function mymodule_init() {
  global $user;

  $result = mymodule_increase_counter($user-&gt;uid); 
  if ($result[0]) {
    // Increase the counter; set the other variables.
  }
  elseif ($result[1] &gt; 86400) {
    // The user didn't log in yesterday.
  }
}

function mymodule_date($timestamp) {
  $date_time = date_create('@' . $timestamp);
  return date_format($date_time, 'Ymd');
}

function mymodule_increase_counter($uid) {
  $last_timestamp = variable_get("mymodule_last_timestamp_$uid", 0);
  if ($last_timestamp == REQUEST_TIME) {
    return array(FALSE, 0);
  }

  $result = array(
    mymodule_date($last_timestamp + 86400) == mymodule_date(REQUEST_TIME),
    REQUEST_TIME - $last_timestamp,
  );
  variable_set("mymodule_last_timestamp_$uid", REQUEST_TIME);

  return $result;
}




// Drupal 6
function mymodule_init() {
  global $user;

  $result = mymodule_increase_counter($user-&gt;uid); 
  if ($result[0]) {
    // Increase the counter; set the other variables.
  }
  elseif ($result[1] &gt; 86400) {
    // The user didn't log in yesterday.
  }
}

function mymodule_increase_counter($uid) {
  $last_timestamp = variable_get("mymodule_last_timestamp_$uid", 0);
  $result = array(FALSE, time() - $last_timestamp);

  if (time() - $last_timestamp &lt; 20) {
    return $result;
  }

  $result[0] = (mymodule_date($last_timestamp + 86400) == mymodule_date(REQUEST_TIME));
  variable_set("mymodule_last_timestamp_$uid", time());

  return $result;
}


If hook_init() is invoked two times in row during the same page request, REQUEST_TIME contains the same value, and the function would return FALSE.

The code in mymodule_increase_counter() is not optimized; it is just to show an example. In a real module, I would rather use a database table where the counter, and the other variables are saved. The reason is that Drupal variables are all loaded in the global variable $conf when Drupal bootstraps (see _drupal_bootstrap_variables(), and variable_initialize()); if you use Drupal variables for that, Drupal would load in memory information about all the users  for which you saved information, when for each requested page there is only one user account saved in the global variable $user.   

If you are counting the number of page visited from the users in consecutive days, then I would implement the following code.

// Drupal 7
function mymodule_init() {
  global $user;

  $result = mymodule_increase_counter($user-&gt;uid); 
  if ($result[0]) {
    // Increase the counter; set the other variables.
  }
  elseif ($result[1] &gt; 86400) {
    // The user didn't log in yesterday.
  }
}

function mymodule_date($timestamp) {
  $date_time = date_create('@' . $timestamp);
  return date_format($date_time, 'Ymd');
}

function mymodule_increase_counter($uid) {
  $last_timestamp = variable_get("mymodule_last_timestamp_$uid", 0);
  if ($last_timestamp == REQUEST_TIME) {
    return array(FALSE, 0);
  }

  $result = array(
    mymodule_date($last_timestamp + 86400) == mymodule_date(REQUEST_TIME),
    REQUEST_TIME - $last_timestamp,
  );
  variable_set("mymodule_last_timestamp_$uid", REQUEST_TIME);

  return $result;
}




// Drupal 6
function mymodule_init() {
  global $user;

  $result = mymodule_increase_counter($user-&gt;uid); 
  if ($result[0]) {
    // Increase the counter; set the other variables.
  }
  elseif ($result[1] &gt; 86400) {
    // The user didn't log in yesterday.
  }
}

function mymodule_increase_counter($uid) {
  $last_timestamp = variable_get("mymodule_last_timestamp_$uid", 0);
  $result = array(FALSE, time() - $last_timestamp);

  if (time() - $last_timestamp &lt; 20) {
    return $result;
  }

  $result[0] = (mymodule_date($last_timestamp + 86400) == mymodule_date(REQUEST_TIME));
  variable_set("mymodule_last_timestamp_$uid", time());

  return $result;
}


You will notice that in my code I don't use $user-&gt;access. The reason is that $user-&gt;access could be updated during Drupal bootstrap, before hook_init() is invoked. The session write handler used from Drupal contains the following code. (See _drupal_session_write().)

// Likewise, do not update access time more than once per 180 seconds.
if ($user-&gt;uid &amp;&amp; REQUEST_TIME - $user-&gt;access &gt; variable_get('session_write_interval', 180)) {
  db_update('users')
    -&gt;fields(array(
    'access' =&gt; REQUEST_TIME,
  ))
    -&gt;condition('uid', $user-&gt;uid)
    -&gt;execute();
}


As for another hook you can use, with Drupal 7 you can use hook_page_alter(); you just don't alter the content of $page, but increase your counter, and change your variables.
On Drupal 6, you could use hook_footer(), the hook called from template_preprocess_page(). You don't return anything, but increase your counter, and change your variables.  

On Drupal 6, and Drupal 7, you could use hook_exit(). Bear in mind that the hook is also invoked when the bootstrap is not complete; the code could not have access to functions defined from modules, or other Drupal functions, and you should first check those functions are available. Some functions are always available from hook_exit(), such as the ones defined in bootstrap.inc, and cache.inc. The difference is that hook_exit() is invoked also for cached pages, while hook_init() is not invoked for cached pages.

Finally, as example of code used from a Drupal module, see statistics_exit(). The Statistics module logs access statistics for a site, and as you see, it uses hook_exit(), not hook_init(). To be able to call the necessary functions, it calls drupal_bootstrap() passing the correct parameter, such as in the following code.

  // When serving cached pages with the 'page_cache_without_database'
  // configuration, system variables need to be loaded. This is a major
  // performance decrease for non-database page caches, but with Statistics
  // module, it is likely to also have 'statistics_enable_access_log' enabled,
  // in which case we need to bootstrap to the session phase anyway.
  drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);




  if (variable_get('statistics_enable_access_log', 0)) {
    drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);

    // For anonymous users unicode.inc will not have been loaded.
    include_once DRUPAL_ROOT . '/includes/unicode.inc';
    // Log this page access.
    db_insert('accesslog')
      -&gt;fields(array(
      'title' =&gt; truncate_utf8(strip_tags(drupal_get_title()), 255), 
      'path' =&gt; truncate_utf8($_GET['q'], 255), 
      'url' =&gt; isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', 
      'hostname' =&gt; ip_address(), 
      'uid' =&gt; $user-&gt;uid, 
      'sid' =&gt; session_id(), 
      'timer' =&gt; (int) timer_read('page'), 
      'timestamp' =&gt; REQUEST_TIME,
    ))
      -&gt;execute();
  }


Update

Maybe there is some confusion about when hook_init() is invoked.  

hook_init() is invoked for each page request, if the page is not cached. It is not invoked once for each page request coming from the same user. If you visit, for example, http://example.com/admin/appearance/update, and then http://example.com/admin/reports/status, hook_init() will be invoked twice: one for each page.
"The hook is invoked twice" means there is a module that executes the following code, once Drupal has completed its bootstrap.

module_invoke_all('init');


If that is the case, then the following implementation of hook_init() would show the same value, twice.

function mymodule_init() {
  watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' =&gt; REQUEST_TIME), WATCHDOG_DEBUG);
}


If your code shown for REQUEST_TIME two values for which the difference is 2 minutes, as in your case, then the hook is not invoked twice, but it is invoked once for each requested page, as it should happen.

REQUEST_TIME is defined in bootstrap.inc with the following line.

define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);


Until the currently requested page is not returned to the browser, the value of REQUEST_TIME doesn't change. If you see a different value, then you are watching the value assigned in a different request page.