hook_init()
viene invocato da Drupal solo una volta per ogni pagina richiesta; è l'ultimo passaggio fatto 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');
}
Se hook_init()
viene eseguito più di una volta, dovresti scoprire perché ciò accade. Per quanto posso vedere, nessuna delle hook_init()
implementazioni in Drupal controlla che venga eseguita due volte (vedi ad esempio system_init () o update_init () ). Se questo è qualcosa che normalmente può accadere con Drupal, allora update_init()
controlla prima se è già stato eseguito.
Se il contatore è il numero di giorni consecutivi in cui un utente ha effettuato l'accesso, preferirei implementare hook_init()
con un codice simile al seguente.
// Drupal 7
function mymodule_init() {
global $user;
$result = mymodule_increase_counter($user->uid);
if ($result[0]) {
// Increase the counter; set the other variables.
}
elseif ($result[1] > 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->uid);
if ($result[0]) {
// Increase the counter; set the other variables.
}
elseif ($result[1] > 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 < 20) {
return $result;
}
$result[0] = (mymodule_date($last_timestamp + 86400) == mymodule_date(REQUEST_TIME));
variable_set("mymodule_last_timestamp_$uid", time());
return $result;
}
Se hook_init()
viene richiamato due volte di fila durante la stessa richiesta di pagina, REQUEST_TIME
contiene lo stesso valore e la funzione restituisce FALSE
.
Il codice in mymodule_increase_counter()
non è ottimizzato; è solo per mostrare un esempio. In un modulo reale, preferirei utilizzare una tabella di database in cui il contatore e le altre variabili vengono salvate. Il motivo è che le variabili Drupal sono tutte caricate nella variabile globale $conf
quando Drupal si avvia (vedi _drupal_bootstrap_variables () e variabile_initialize () ); se usi le variabili Drupal per questo, Drupal carica in memoria le informazioni su tutti gli utenti per i quali hai salvato le informazioni, quando per ogni pagina richiesta c'è un solo account utente salvato nella variabile globale $user
.
Se stai contando il numero di pagine visitate dagli utenti in giorni consecutivi, implementerei il seguente codice.
// Drupal 7
function mymodule_init() {
global $user;
$result = mymodule_increase_counter($user->uid);
if ($result[0]) {
// Increase the counter; set the other variables.
}
elseif ($result[1] > 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->uid);
if ($result[0]) {
// Increase the counter; set the other variables.
}
elseif ($result[1] > 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 < 20) {
return $result;
}
$result[0] = (mymodule_date($last_timestamp + 86400) == mymodule_date(REQUEST_TIME));
variable_set("mymodule_last_timestamp_$uid", time());
return $result;
}
Noterai che nel mio codice non uso $user->access
. Il motivo è che $user->access
potrebbe essere aggiornato durante l'avvio bootuprap di Drupal, prima che hook_init()
venga invocato. Il gestore di scrittura della sessione utilizzato da Drupal contiene il seguente codice. (Vedi _drupal_session_write () .)
// Likewise, do not update access time more than once per 180 seconds.
if ($user->uid && REQUEST_TIME - $user->access > variable_get('session_write_interval', 180)) {
db_update('users')
->fields(array(
'access' => REQUEST_TIME,
))
->condition('uid', $user->uid)
->execute();
}
Come per un altro hook che puoi usare, con Drupal 7 puoi usare hook_page_alter () ; semplicemente non modifichi il contenuto di $page
, ma aumenti il tuo contatore e cambi le tue variabili.
Su Drupal 6, puoi usare hook_footer () , l'hook chiamato da template_preprocess_page () . Non si restituisce nulla, ma si aumenta il contatore e si modificano le variabili.
Su Drupal 6 e Drupal 7, puoi usare hook_exit () . Tenere presente che l'hook viene anche richiamato quando il bootstrap non è completo; il codice non può avere accesso alle funzioni definite dai moduli o altre funzioni Drupal e dovresti prima verificare che tali funzioni siano disponibili. Alcune funzioni sono sempre disponibili da hook_exit()
, come quelle definite in bootstrap.inc e cache.inc . La differenza è che hook_exit()
viene invocata anche per le pagine memorizzate nella cache, mentre hook_init()
non viene invocata per le pagine memorizzate nella cache.
Infine, come esempio di codice utilizzato da un modulo Drupal, vedere statistics_exit () . Il modulo di statistiche registra statistiche di accesso per un sito, e come potete vedere, utilizza hook_exit()
e non hook_init()
. Per poter chiamare le funzioni necessarie, chiama drupal_bootstrap () passando il parametro corretto, come nel codice seguente.
// 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')
->fields(array(
'title' => truncate_utf8(strip_tags(drupal_get_title()), 255),
'path' => truncate_utf8($_GET['q'], 255),
'url' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
'hostname' => ip_address(),
'uid' => $user->uid,
'sid' => session_id(),
'timer' => (int) timer_read('page'),
'timestamp' => REQUEST_TIME,
))
->execute();
}
Aggiornare
Forse c'è qualche confusione su quando hook_init()
viene invocato.
hook_init()
viene richiamato per ogni richiesta di pagina, se la pagina non viene memorizzata nella cache. Non viene invocato una volta per ogni richiesta di pagina proveniente dallo stesso utente. Se visiti, ad esempio, http://example.com/admin/appearance/update e quindi http://example.com/admin/reports/status , hook_init()
verrà richiamato due volte: uno per ogni pagina.
"L'hook viene invocato due volte" significa che c'è un modulo che esegue il seguente codice, una volta che Drupal ha completato il suo bootstrap.
module_invoke_all('init');
In tal caso, la seguente implementazione hook_init()
mostrerebbe lo stesso valore, due volte.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Se il tuo codice viene visualizzato per REQUEST_TIME
due valori per i quali la differenza è di 2 minuti, come nel tuo caso, l'hook non viene invocato due volte, ma viene invocato una volta per ogni pagina richiesta, come dovrebbe accadere.
REQUEST_TIME
è definito in bootstrap.inc con la seguente riga.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Fino a quando la pagina attualmente richiesta non viene restituita al browser, il valore di REQUEST_TIME
non cambia. Se vedi un valore diverso, stai osservando il valore assegnato in una pagina di richiesta diversa.