hook_init()
é invocado pelo Drupal apenas uma vez para cada página solicitada; é o último passo realizado em _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()
estiver sendo executado mais de uma vez, você deve descobrir por que isso acontece. Até onde posso ver, nenhuma das hook_init()
implementações no Drupal verifica se está sendo executada duas vezes (veja, por exemplo, system_init () ou update_init () ). Se isso é algo que normalmente pode acontecer com o Drupal, update_init()
verifique primeiro se ele já foi executado.
Se o contador for o número de dias consecutivos em que um usuário efetuou login, eu preferiria implementar hook_init()
com código semelhante ao seguinte.
// 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()
for chamado duas vezes consecutivas durante a mesma solicitação de página, REQUEST_TIME
conterá o mesmo valor e a função retornará FALSE
.
O código in mymodule_increase_counter()
não é otimizado; é apenas para mostrar um exemplo. Em um módulo real, eu preferiria usar uma tabela de banco de dados em que o contador e as outras variáveis sejam salvas. O motivo é que as variáveis Drupal são todas carregadas na variável global $conf
quando o Drupal é iniciado ( consulte _drupal_bootstrap_variables () e variable_initialize () ); se você usar variáveis do Drupal para isso, o Drupal carregará na memória informações sobre todos os usuários para os quais você salvou as informações, quando para cada página solicitada houver apenas uma conta de usuário salva na variável global $user
.
Se você estiver contando o número de páginas visitadas dos usuários em dias consecutivos, implementaria o código a seguir.
// 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;
}
Você notará que no meu código eu não uso $user->access
. O motivo é que $user->access
poderia ser atualizado durante a inicialização do Drupal, antes de hook_init()
ser chamado. O manipulador de gravação de sessão usado no Drupal contém o seguinte código. (Consulte _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();
}
Quanto a outro gancho que você pode usar, com o Drupal 7 você pode usar hook_page_alter () ; você simplesmente não altera o conteúdo de $page
, mas aumenta seu contador e altera suas variáveis.
No Drupal 6, você pode usar hook_footer () , o gancho chamado de template_preprocess_page () . Você não retorna nada, mas aumenta seu contador e altera suas variáveis.
No Drupal 6 e Drupal 7, você pode usar hook_exit () . Lembre-se de que o gancho também é chamado quando a inicialização não está completa; o código não pode ter acesso a funções definidas a partir de módulos ou outras funções do Drupal, e você deve primeiro verificar se essas funções estão disponíveis. Algumas funções estão sempre disponíveis hook_exit()
, como as definidas em bootstrap.inc e cache.inc . A diferença é que hook_exit()
é invocado também para páginas em cache, enquanto hook_init()
não é invocado para páginas em cache.
Por fim, como exemplo de código usado em um módulo Drupal, consulte statistics_exit () . O módulo Estatísticas registra as estatísticas de acesso de um site e, como você vê, ele usa hook_exit()
, não hook_init()
. Para poder chamar as funções necessárias, ele chama drupal_bootstrap () passando o parâmetro correto, como no código a seguir.
// 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();
}
Atualizar
Talvez haja alguma confusão sobre quando hook_init()
é invocado.
hook_init()
é chamado para cada solicitação de página, se a página não estiver em cache. Não é chamado uma vez para cada solicitação de página proveniente do mesmo usuário. Se você visitar, por exemplo, http://example.com/admin/appearance/update e http://example.com/admin/reports/status , hook_init()
será chamado duas vezes: um para cada página.
"O gancho é chamado duas vezes" significa que há um módulo que executa o código a seguir, depois que o Drupal conclui sua inicialização.
module_invoke_all('init');
Se for esse o caso, a seguinte implementação de hook_init()
mostraria o mesmo valor, duas vezes.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Se o seu código for exibido para REQUEST_TIME
dois valores para os quais a diferença é de 2 minutos, como no seu caso, o gancho não será chamado duas vezes, mas será chamado uma vez para cada página solicitada, como deve acontecer.
REQUEST_TIME
é definido em bootstrap.inc com a seguinte linha.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Até que a página solicitada no momento não seja retornada ao navegador, o valor de REQUEST_TIME
não será alterado. Se você vir um valor diferente, estará observando o valor atribuído em uma página de solicitação diferente.