J'utilise hook_init()
pour vérifier le dernier temps d'accès des utilisateurs. Si le dernier temps d'accès est hier, j'incrémente un compteur et règle quelques variables.
Le problème est qu'il hook_init()
est parfois exécuté plus d'une fois (je peux le voir en utilisant dsm()
) pour le même chargement de page, donc mon code est exécuté plusieurs fois, ce qui entraîne des variables erronées.
Pourquoi est hook_init()
exécuté plus d'une fois?
Quelle serait la meilleure approche de mon problème? Dois-je utiliser un autre crochet?
J'ai creusé un peu plus à ce sujet:
je recherche des appels à hook_init () (recherche de chaîne module_invoke_all('init');
) mais ne trouve que l'appel principal). Je ne sais pas si cela peut être appelé différemment.
Ceci est mon 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.')');
}
et voici la sortie:
1st execution
REQUEST_TIME: 09/07/2012 11:20:32 (1341822032)
puis, changé le message dsm () dsm('2nd execution');
et exécuté à nouveau, voici la sortie:
1st execution
REQUEST_TIME: 09/07/2012 11:20:34 (1341822034)
2nd execution
REQUEST_TIME: 09/07/2012 11:22:28 (1341822148)
Vous pouvez voir que le code est exécuté deux fois. Cependant, la première fois exécute une ancienne copie du code et la deuxième fois la copie mise à jour. Il y a aussi un décalage horaire de 2 secondes.
Ceci est une version d7 avec php 5.3.10
REQUEST_TIME
serait la même.REQUEST_TIME
provient de la même demande de page, sa valeur est la même; il n'y a même pas de différence de deux secondes. Vérifiez qu'aucun code ne modifie la valeur deREQUEST_TIME
.Réponses:
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 () .Si
hook_init()
est exécuté plusieurs fois, vous devez découvrir pourquoi cela se produit. Pour autant que je puisse voir, aucune deshook_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.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 retourneraFALSE
.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.
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 () .)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'ellehook_exit()
est invoquée également pour les pages en cache, tandis qu'ellehook_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()
, nonhook_init()
. Pour pouvoir appeler les fonctions nécessaires, il appelle drupal_bootstrap () en passant le paramètre correct, comme dans le code suivant.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.
Si tel est le cas, l'implémentation suivante de
hook_init()
afficherait la même valeur, deux fois.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.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.la source
Je me souviens que cela se passait beaucoup dans Drupal 6 (je ne sais pas si c'est toujours le cas dans Drupal 7), mais je n'ai jamais su pourquoi. Je semble me souvenir d'avoir vu quelque part que le noyau Drupal n'appelle pas ce crochet deux fois.
J'ai toujours trouvé que le moyen le plus simple était d'utiliser une variable statique pour voir si le code avait déjà été exécuté:
Cela garantira qu'il ne sera exécuté qu'une seule fois dans un seul chargement de page.
la source
hook_init()
implémentations, et certains d'entre eux éviteraient volontiers d'être exécutés deux fois de suite. Il est également probable que l'OP souhaitehook_init()
être exécuté une fois par jour, si le compteur compte le nombre de jours consécutifs de connexion des utilisateurs sur le site.hook_init
vérifie s'il est déjà exécuté une fois pour la journée et annule si c'est le cas. Ensuite, le tout devient un non-problème de toute façonVous pourriez trouver hook_init () est appelé plusieurs fois s'il y a un AJAX sur la page (ou si vous chargez des images à partir d'un répertoire privé - bien que je n'en sois pas sûr). Il y a quelques modules qui utilisent AJAX pour aider à contourner la mise en cache des pages pour certains éléments par exemple - le moyen le plus simple de vérifier est d'ouvrir le moniteur net dans le débogueur de votre choix (firefox ou inspecteur web) et d'avoir un regard pour voir si des demandes sont faites qui pourraient déclencher le processus d'amorçage.
Vous n'aurez que le dpm () lors du chargement de la page suivante si c'est un appel AJAX. Supposons donc que vous actualisiez la page 5 minutes plus tard, vous obtiendrez l'appel AJAX du message d'init d'il y a 5 minutes ainsi que du nouveau.
Une alternative à hook_init () est hook_boot () qui est appelée avant toute mise en cache. Aucun module n'est encore chargé, donc vous n'avez vraiment pas beaucoup de pouvoir ici à part définir des variables globales et exécuter quelques fonctions Drupal. Il est utile pour contourner la mise en cache de niveau normal (mais ne contournera pas la mise en cache agressive).
la source
Dans mon cas, ce comportement est dû au module du menu d'administration (admin_menu).
hook_init n'était pas appelé à chaque demande, mais le menu d'administration entraînerait le chargement de / js / admin_menu / cache / 94614e34b017b19a78878d7b96ccab55 par le navigateur de l'utilisateur très peu de temps après la demande principale, déclenchant un autre amorçage drupal.
Il y aura d'autres modules qui font des choses similaires, mais admin_menu est probablement l'un des plus couramment déployés.
la source