hook_init()
được Drupal gọi chỉ một lần cho mỗi trang được yêu cầu; đây là bước cuối cùng được thực hiện trong _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');
}
Nếu hook_init()
đang được thực hiện nhiều lần, bạn nên khám phá lý do tại sao điều đó xảy ra. Theo như tôi có thể thấy, không có cách hook_init()
triển khai nào trong Drupal kiểm tra nó đang được thực thi hai lần (xem ví dụ system_init () hoặc update_init () ). Nếu đó là điều thường có thể xảy ra với Drupal, thì update_init()
trước tiên hãy kiểm tra xem nó đã được thực hiện chưa.
Nếu bộ đếm là số ngày liên tiếp người dùng đăng nhập, tôi thà thực hiện hook_init()
với mã tương tự như sau.
// 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;
}
Nếu hook_init()
được gọi hai lần liên tiếp trong cùng một yêu cầu trang, REQUEST_TIME
chứa cùng một giá trị và hàm sẽ trả về FALSE
.
Mã trong mymodule_increase_counter()
không được tối ưu hóa; nó chỉ là một ví dụ Trong một mô-đun thực, tôi muốn sử dụng bảng cơ sở dữ liệu trong đó bộ đếm và các biến khác được lưu. Lý do là tất cả các biến Drupal được tải trong biến toàn cục $conf
khi bootstraps Drupal (xem _drupal_bootstrap_variables () và biến_initialize () ); nếu bạn sử dụng biến Drupal cho điều đó, Drupal sẽ tải thông tin bộ nhớ về tất cả người dùng mà bạn đã lưu thông tin, khi mỗi trang được yêu cầu chỉ có một tài khoản người dùng được lưu trong biến toàn cục $user
.
Nếu bạn đang đếm số lượng trang được truy cập từ người dùng trong những ngày liên tiếp, thì tôi sẽ triển khai mã sau đây.
// 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;
}
Bạn sẽ nhận thấy rằng trong mã của tôi, tôi không sử dụng $user->access
. Lý do là $user->access
có thể được cập nhật trong bootstrap Drupal, trước khi hook_init()
được gọi. Trình xử lý ghi phiên được sử dụng từ Drupal chứa mã sau đây. (Xem _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();
}
Đối với một hook khác mà bạn có thể sử dụng, với Drupal 7, bạn có thể sử dụng hook_page_alter () ; bạn chỉ không thay đổi nội dung của $page
, nhưng tăng bộ đếm của bạn và thay đổi các biến của bạn.
Trên Drupal 6, bạn có thể sử dụng hook_footer () , hook được gọi từ template_pre process_page () . Bạn không trả lại bất cứ điều gì, nhưng tăng bộ đếm của bạn và thay đổi các biến của bạn.
Trên Drupal 6 và Drupal 7, bạn có thể sử dụng hook_exit () . Hãy nhớ rằng hook cũng được gọi khi bootstrap chưa hoàn thành; mã không thể có quyền truy cập vào các chức năng được xác định từ các mô-đun hoặc các chức năng Drupal khác và trước tiên bạn nên kiểm tra các chức năng đó có sẵn. Một số hàm luôn có sẵn từ hook_exit()
, chẳng hạn như các hàm được xác định trong bootstrap.inc và cache.inc . Sự khác biệt là hook_exit()
cũng được gọi cho các trang được lưu trong bộ nhớ cache, trong khi hook_init()
không được gọi cho các trang được lưu trong bộ nhớ cache.
Cuối cùng, như ví dụ về mã được sử dụng từ mô-đun Drupal, xem stats_exit () . Mô-đun Thống kê ghi lại số liệu thống kê truy cập cho một trang web và như bạn thấy, nó sử dụng hook_exit()
chứ không phải hook_init()
. Để có thể gọi các hàm cần thiết, nó gọi drupal_bootstrap () truyền tham số chính xác, chẳng hạn như trong đoạn mã sau.
// 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();
}
Cập nhật
Có thể có một số nhầm lẫn về khi hook_init()
được viện dẫn.
hook_init()
được gọi cho mỗi yêu cầu trang, nếu trang không được lưu trữ. Nó không được gọi một lần cho mỗi yêu cầu trang đến từ cùng một người dùng. Ví dụ: nếu bạn truy cập http://example.com/admin/appparent/update và sau đó http://example.com/admin/reports/status , hook_init()
sẽ được gọi hai lần: một cho mỗi trang.
"Hook được gọi hai lần" có nghĩa là có một mô-đun thực thi mã sau đây, khi Drupal đã hoàn thành bootstrap của nó.
module_invoke_all('init');
Nếu đó là trường hợp, thì việc thực hiện sau đây hook_init()
sẽ hiển thị cùng một giá trị, hai lần.
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
Nếu mã của bạn hiển thị cho REQUEST_TIME
hai giá trị có chênh lệch là 2 phút, như trong trường hợp của bạn, thì hook không được gọi hai lần, nhưng nó được gọi một lần cho mỗi trang được yêu cầu, vì nó sẽ xảy ra.
REQUEST_TIME
được định nghĩa trong bootstrap.inc với dòng sau.
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Cho đến khi trang hiện được yêu cầu không được trả về trình duyệt, giá trị của REQUEST_TIME
không thay đổi. Nếu bạn thấy một giá trị khác, thì bạn đang xem giá trị được chỉ định trong một trang yêu cầu khác.