hook_init()
Drupal invoca solo una vez para cada página solicitada; Es el último paso realizado en _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()
se ejecuta más de una vez, debe descubrir por qué sucede eso. Hasta donde puedo ver, ninguna de las hook_init()
implementaciones en Drupal comprueba que se esté ejecutando dos veces (ver, por ejemplo, system_init () o update_init () ). Si eso es algo que normalmente puede suceder con Drupal, update_init()
primero verificará si ya se ha ejecutado.
Si el contador es el número de días consecutivos que un usuario inició sesión, preferiría implementar un hook_init()
código similar al siguiente.
// 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()
se invoca dos veces seguidas durante la misma solicitud de página, REQUEST_TIME
contiene el mismo valor y la función volvería FALSE
.
El código mymodule_increase_counter()
no está optimizado; es solo para mostrar un ejemplo. En un módulo real, prefiero usar una tabla de base de datos donde se guardan el contador y las otras variables. La razón es que todas las variables de Drupal se cargan en la variable global $conf
cuando los bootstraps de Drupal (ver _drupal_bootstrap_variables () y variable_initialize () ); si usa variables de Drupal para eso, Drupal cargaría en la información de la memoria acerca de todos los usuarios para los que guardó información, cuando para cada página solicitada solo hay una cuenta de usuario guardada en la variable global $user
.
Si está contando el número de páginas visitadas por los usuarios en días consecutivos, entonces implementaría el siguiente código.
// 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;
}
Notarás que en mi código no uso $user->access
. La razón es que $user->access
podría actualizarse durante el arranque de Drupal, antes de que hook_init()
se invoque. El controlador de escritura de sesión utilizado desde Drupal contiene el siguiente código. (Ver _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();
}
En cuanto a otro gancho que puede usar, con Drupal 7 puede usar hook_page_alter () ; simplemente no altera el contenido de $page
, sino que aumenta su contador y cambia sus variables.
En Drupal 6, puede usar hook_footer () , el gancho llamado desde template_preprocess_page () . No devuelve nada, pero aumenta su contador y cambia sus variables.
En Drupal 6 y Drupal 7, puede usar hook_exit () . Tenga en cuenta que el gancho también se invoca cuando el bootstrap no está completo; el código no puede tener acceso a las funciones definidas desde los módulos u otras funciones de Drupal, y primero debe verificar que esas funciones estén disponibles. Algunas funciones siempre están disponibles hook_exit()
, como las definidas en bootstrap.inc y cache.inc . La diferencia es que hook_exit()
se invoca también para páginas en caché, mientras hook_init()
que no se invoca para páginas en caché.
Finalmente, como ejemplo de código utilizado desde un módulo de Drupal, vea stats_exit () . El módulo Estadísticas registra las estadísticas de acceso de un sitio y, como puede ver, usa hook_exit()
no hook_init()
. Para poder llamar a las funciones necesarias, llama a drupal_bootstrap () pasando el parámetro correcto, como en el siguiente código.
// 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();
}
Actualizar
Tal vez haya cierta confusión sobre cuándo hook_init()
se invoca.
hook_init()
se invoca para cada solicitud de página, si la página no está en caché. No se invoca una vez por cada solicitud de página que provenga del mismo usuario. Si visita, por ejemplo, http://example.com/admin/appearance/update , y luego http://example.com/admin/reports/status , hook_init()
se invocará dos veces: una para cada página.
"El gancho se invoca dos veces" significa que hay un módulo que ejecuta el siguiente código, una vez que Drupal ha completado su arranque.
module_invoke_all('init');
Si ese es el caso, la siguiente implementación de hook_init()
mostraría el mismo valor, dos veces.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Si su código se muestra para REQUEST_TIME
dos valores para los cuales la diferencia es de 2 minutos, como en su caso, el gancho no se invoca dos veces, sino que se invoca una vez para cada página solicitada, como debería suceder.
REQUEST_TIME
se define en bootstrap.inc con la siguiente línea.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Hasta que la página solicitada actualmente no se devuelva al navegador, el valor de REQUEST_TIME
no cambia. Si ve un valor diferente, está viendo el valor asignado en una página de solicitud diferente.