hook_init()
n'est invoqué par Drupal qu'une seule fois pour chaque page demandée; c'est la dernière étape effectuée dans _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');
}
Si hook_init()
est exécuté plusieurs fois, vous devez découvrir pourquoi cela se produit. Pour autant que je puisse voir, aucune des hook_init()
implémentations de Drupal ne vérifie qu'il est exécuté deux fois (voir par exemple system_init () ou update_init () ). Si c'est quelque chose qui peut normalement se produire avec Drupal, update_init()
vérifiez d'abord s'il a déjà été exécuté.
Si le compteur est le nombre de jours consécutifs qu'un utilisateur s'est connecté, je préfère implémenter hook_init()
avec un code similaire au suivant.
// 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;
}
Si hook_init()
est invoqué deux fois de suite lors de la même demande de page, REQUEST_TIME
contient la même valeur et la fonction retournera FALSE
.
Le code mymodule_increase_counter()
n'est pas optimisé; c'est juste pour montrer un exemple. Dans un vrai module, je préfère utiliser une table de base de données où le compteur et les autres variables sont enregistrés. La raison en est que les variables Drupal sont toutes chargées dans la variable globale $conf
lorsque Drupal démarre ( voir _drupal_bootstrap_variables () et variable_initialize () ); si vous utilisez des variables Drupal pour cela, Drupal chargerait en mémoire des informations sur tous les utilisateurs pour lesquels vous avez enregistré des informations, alors que pour chaque page demandée, il n'y a qu'un seul compte d'utilisateur enregistré dans la variable globale $user
.
Si vous comptez le nombre de pages visitées par les utilisateurs au cours des jours consécutifs, j'implémenterais le code suivant.
// 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;
}
Vous remarquerez que dans mon code je n'utilise pas $user->access
. La raison en est que cela $user->access
pourrait être mis à jour pendant le bootstrap Drupal, avant d' hook_init()
être invoqué. Le gestionnaire d'écriture de session utilisé à partir de Drupal contient le code suivant. (Voir _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();
}
Quant à un autre hook que vous pouvez utiliser, avec Drupal 7, vous pouvez utiliser hook_page_alter () ; vous ne modifiez tout simplement pas le contenu de $page
, mais augmentez votre compteur et modifiez vos variables.
Sur Drupal 6, vous pouvez utiliser hook_footer () , le hook appelé depuis template_preprocess_page () . Vous ne retournez rien, mais augmentez votre compteur et modifiez vos variables.
Sur Drupal 6 et Drupal 7, vous pouvez utiliser hook_exit () . Gardez à l'esprit que le crochet est également invoqué lorsque le bootstrap n'est pas terminé; le code ne peut pas avoir accès aux fonctions définies à partir des modules ou à d'autres fonctions Drupal, et vous devez d'abord vérifier que ces fonctions sont disponibles. Certaines fonctions sont toujours disponibles à partir de hook_exit()
, comme celles définies dans bootstrap.inc et cache.inc . La différence est qu'elle hook_exit()
est invoquée également pour les pages en cache, tandis qu'elle hook_init()
n'est pas invoquée pour les pages en cache.
Enfin, comme exemple de code utilisé à partir d'un module Drupal, voir statistics_exit () . Le module Statistiques enregistre les statistiques d'accès pour un site, et comme vous le voyez, il utilise hook_exit()
, non hook_init()
. Pour pouvoir appeler les fonctions nécessaires, il appelle drupal_bootstrap () en passant le paramètre correct, comme dans le code suivant.
// 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();
}
Mise à jour
Il y a peut-être une certaine confusion sur le moment où hook_init()
est invoqué.
hook_init()
est invoqué pour chaque demande de page, si la page n'est pas mise en cache. Il n'est pas invoqué une seule fois pour chaque demande de page provenant du même utilisateur. Si vous visitez, par exemple, http://example.com/admin/appearance/update , puis http://example.com/admin/reports/status , hook_init()
sera invoqué deux fois: un pour chaque page.
«Le hook est appelé deux fois» signifie qu'il existe un module qui exécute le code suivant, une fois que Drupal a terminé son amorçage.
module_invoke_all('init');
Si tel est le cas, l'implémentation suivante de hook_init()
afficherait la même valeur, deux fois.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Si votre code s'affiche pour REQUEST_TIME
deux valeurs pour lesquelles la différence est de 2 minutes, comme dans votre cas, le crochet n'est pas appelé deux fois, mais il est appelé une fois pour chaque page demandée, comme cela devrait se produire.
REQUEST_TIME
est défini dans bootstrap.inc avec la ligne suivante.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Tant que la page actuellement demandée n'est pas renvoyée au navigateur, la valeur de REQUEST_TIME
ne change pas. Si vous voyez une valeur différente, vous regardez la valeur attribuée dans une autre page de demande.