hook_init()
вызывается Drupal только один раз для каждой запрашиваемой страницы; это последний шаг, выполненный в _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');
}
Если hook_init()
выполняется более одного раза, вы должны выяснить, почему это происходит. Насколько я могу судить, ни один из hook_init()
реализаций в Drupal проверки она исполняется дважды (см, например , system_init () , или update_init () ). Если это то, что обычно происходит с Drupal, то update_init()
сначала проверит, выполнено ли оно уже.
Если счетчик - это число последовательных дней, когда пользователь вошел в систему, я бы предпочел реализовать hook_init()
код, подобный следующему.
// 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;
}
Если hook_init()
вызывается два раза подряд во время одного и того же запроса страницы, REQUEST_TIME
содержит одно и то же значение, и функция вернется FALSE
.
Код в mymodule_increase_counter()
не оптимизирован; это просто чтобы показать пример. В реальном модуле я бы предпочел использовать таблицу базы данных, где сохраняются счетчик и другие переменные. Причина в том, что все переменные Drupal загружаются в глобальную переменную $conf
при загрузке Drupal (см. _Drupal_bootstrap_variables () и variable_initialize () ); если вы используете для этого переменные Drupal, Drupal будет загружать в память информацию обо всех пользователях, для которых вы сохранили информацию, когда для каждой запрашиваемой страницы в глобальной переменной сохраняется только одна учетная запись пользователя $user
.
Если вы подсчитываете количество посещенных пользователями страниц в последовательные дни, я бы применил следующий код.
// 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;
}
Вы заметите, что в моем коде я не использую $user->access
. Причина в том, что это $user->access
может быть обновлено во время начальной загрузки Drupal, прежде чем hook_init()
вызываться. Обработчик записи сеанса, используемый в Drupal, содержит следующий код. (Смотрите _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();
}
Что касается другого хука, который вы можете использовать, с Drupal 7 вы можете использовать hook_page_alter () ; вы просто не изменяете содержимое $page
, а увеличиваете счетчик и меняете свои переменные.
В Drupal 6 вы можете использовать hook_footer () , ловушку, вызываемую из template_preprocess_page () . Вы ничего не возвращаете, но увеличиваете свой счетчик и меняете свои переменные.
На Drupal 6 и Drupal 7 вы можете использовать hook_exit () . Имейте в виду, что крюк также вызывается, когда начальная загрузка не завершена; код не может иметь доступа к функциям, определенным из модулей, или другим функциям Drupal, и вы должны сначала проверить, доступны ли эти функции. Некоторые функции всегда доступны из hook_exit()
, например, определенные в bootstrap.inc и cache.inc . Разница в том, что hook_exit()
он вызывается также для кэшированных страниц, а hook_init()
не для кэшированных страниц.
Наконец, в качестве примера кода, используемого из модуля Drupal, см. Statistics_exit () . Модуль Статистика регистрирует статистику доступа для сайта, и, как вы видите, он использует hook_exit()
, а не hook_init()
. Чтобы иметь возможность вызывать необходимые функции, он вызывает drupal_bootstrap (), передавая правильный параметр, например, в следующем коде.
// 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();
}
Обновить
Может быть, есть некоторая путаница о том, когда hook_init()
вызывается.
hook_init()
вызывается для каждого запроса страницы, если страница не кэширована. Он не вызывается один раз для каждого запроса страницы от одного и того же пользователя. Если вы посетите, например, http://example.com/admin/appearance/update , а затем http://example.com/admin/reports/status , hook_init()
то вызовется дважды: по одному для каждой страницы.
«Хук вызывается дважды» означает, что есть модуль, который выполняет следующий код, как только Drupal завершил свою загрузку.
module_invoke_all('init');
Если это так, то следующая реализация hook_init()
покажет одно и то же значение дважды.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Если ваш код показан для REQUEST_TIME
двух значений, для которых разница составляет 2 минуты, как в вашем случае, то ловушка не вызывается дважды, а вызывается один раз для каждой запрашиваемой страницы, как и должно быть.
REQUEST_TIME
определяется в bootstrap.inc следующей строкой.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Пока запрашиваемая в данный момент страница не возвращается в браузер, значение REQUEST_TIME
не изменяется. Если вы видите другое значение, то вы смотрите значение, назначенное на другой странице запроса.