hook_init()
wird von Drupal nur einmal für jede angeforderte Seite aufgerufen; Dies ist der letzte Schritt 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');
}
Wenn hook_init()
es mehr als einmal ausgeführt wird, sollten Sie herausfinden, warum dies geschieht. Soweit ich sehen kann, hook_init()
überprüft keine der Implementierungen in Drupal, ob sie zweimal ausgeführt wird (siehe zum Beispiel system_init () oder update_init () ). Wenn dies normalerweise mit Drupal passieren kann, update_init()
prüfen Sie zunächst, ob es bereits ausgeführt wurde.
Wenn der Zähler die Anzahl der aufeinanderfolgenden Tage ist, an denen sich ein Benutzer angemeldet hat, würde ich lieber einen hook_init()
Code implementieren , der dem folgenden ähnlich ist.
// 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;
}
Wenn hook_init()
es während derselben Seitenanforderung zweimal hintereinander aufgerufen wird, REQUEST_TIME
denselben Wert enthält und die Funktion zurückgegeben wird FALSE
.
Der Code in mymodule_increase_counter()
ist nicht optimiert. Es soll nur ein Beispiel zeigen. In einem realen Modul würde ich lieber eine Datenbanktabelle verwenden, in der der Zähler und die anderen Variablen gespeichert sind. Der Grund dafür ist, dass $conf
beim Drupal-Bootstraps alle Drupal-Variablen in die globale Variable geladen werden (siehe _drupal_bootstrap_variables () und variable_initialize () ). Wenn Sie dafür Drupal-Variablen verwenden, lädt Drupal Speicherinformationen über alle Benutzer, für die Sie Informationen gespeichert haben, wenn für jede angeforderte Seite nur ein Benutzerkonto in der globalen Variablen gespeichert ist $user
.
Wenn Sie die Anzahl der von den Benutzern an aufeinanderfolgenden Tagen besuchten Seiten zählen, würde ich den folgenden Code implementieren.
// 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;
}
Sie werden feststellen, dass ich in meinem Code keine verwende $user->access
. Der Grund ist, dass $user->access
dies während des Drupal-Bootstraps aktualisiert werden könnte, bevor hook_init()
es aufgerufen wird. Der von Drupal verwendete Session Write Handler enthält den folgenden Code. (Siehe _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();
}
Für einen anderen Hook, den Sie verwenden können, können Sie mit Drupal 7 hook_page_alter () verwenden . Sie ändern einfach nicht den Inhalt von $page
, sondern erhöhen Ihren Zähler und ändern Ihre Variablen.
In Drupal 6 können Sie hook_footer () verwenden , den Hook, der von template_preprocess_page () aufgerufen wird . Sie geben nichts zurück, sondern erhöhen Ihren Zähler und ändern Ihre Variablen.
Auf Drupal 6 und Drupal 7 können Sie hook_exit () verwenden . Beachten Sie, dass der Hook auch aufgerufen wird, wenn der Bootstrap nicht vollständig ist. Der Code konnte keinen Zugriff auf Funktionen haben, die von Modulen oder anderen Drupal-Funktionen definiert wurden, und Sie sollten zuerst überprüfen, ob diese Funktionen verfügbar sind. Einige Funktionen sind immer verfügbar hook_exit()
, z. B. die in bootstrap.inc und cache.inc definierten . Der Unterschied besteht darin, dass er hook_exit()
auch für zwischengespeicherte Seiten hook_init()
aufgerufen wird , während er nicht für zwischengespeicherte Seiten aufgerufen wird.
Ein Beispiel für Code, der von einem Drupal-Modul verwendet wird, finden Sie unter statistics_exit () . Das Statistikmodul protokolliert Zugriffsstatistiken für eine Site und verwendet, wie Sie sehen hook_exit()
, nicht hook_init()
. Um die erforderlichen Funktionen aufrufen zu können, wird drupal_bootstrap () aufgerufen, wobei der richtige Parameter übergeben wird, wie im folgenden 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')
->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();
}
Aktualisieren
Vielleicht gibt es einige Verwirrung darüber, wann hook_init()
aufgerufen wird.
hook_init()
wird für jede Seitenanforderung aufgerufen, wenn die Seite nicht zwischengespeichert ist. Es wird nicht einmal für jede Seitenanforderung aufgerufen, die vom selben Benutzer stammt. Wenn Sie besuchen zum Beispiel http://example.com/admin/appearance/update und dann http://example.com/admin/reports/status , hook_init()
wird zweimal aufgerufen werden: eine für jede Seite.
"Der Hook wird zweimal aufgerufen" bedeutet, dass es ein Modul gibt, das den folgenden Code ausführt, sobald Drupal seinen Bootstrap abgeschlossen hat.
module_invoke_all('init');
Wenn dies der Fall ist, würde die folgende Implementierung von hook_init()
zweimal denselben Wert anzeigen.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Wenn Ihr Code für REQUEST_TIME
zwei Werte angezeigt wird , für die die Differenz wie in Ihrem Fall 2 Minuten beträgt, wird der Hook nicht zweimal aufgerufen, sondern einmal für jede angeforderte Seite, wie es passieren sollte.
REQUEST_TIME
wird in bootstrap.inc mit der folgenden Zeile definiert.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Bis die aktuell angeforderte Seite nicht an den Browser zurückgegeben wird, REQUEST_TIME
ändert sich der Wert von nicht. Wenn Sie einen anderen Wert sehen, beobachten Sie den auf einer anderen Anforderungsseite zugewiesenen Wert.