hook_init()
リクエストされたページごとに1回だけ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()
、それが2回実行されている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()
同じページ要求中に2回続けて呼び出された場合REQUEST_TIME
、同じ値が含まれ、関数はを返しFALSE
ます。
のコードmymodule_increase_counter()
は最適化されていません。例を示すだけです。実際のモジュールでは、カウンターと他の変数が保存されているデータベーステーブルを使用します。その理由は、Drupalの$conf
ブートストラップ(_drupal_bootstrap_variables()とvariable_initialize()を参照)すると、Drupal変数がすべてグローバル変数にロードされるためです。Drupal変数を使用する場合、Drupalは、情報を保存したすべてのユーザーに関する情報をメモリにロードします。リクエストされた各ページについて、グローバル変数に保存されているユーザーアカウントは1つだけです$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では、template_preprocess_page()から呼び出されるフックであるhook_footer()を使用できます。何も返しませんが、カウンターを増やして変数を変更します。
Drupal 6およびDrupal 7では、hook_exit()を使用できます。フックは、ブートストラップが完了していないときにも呼び出されることに注意してください。モジュールから定義された関数や他のDrupal関数にコードがアクセスできなかったため、最初にそれらの関数が使用可能かどうかを確認してください。bootstrap.incやcache.incでhook_exit()
定義されている関数など、一部の関数は常にから使用できます。違いは、キャッシュされたページに対しては呼び出されず、キャッシュされたページに対しては呼び出されないことです。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()
ページがキャッシュされていない場合、ページリクエストごとに呼び出されます。同じユーザーからのページ要求ごとに1回呼び出されることはありません。あなたが訪問した場合、例えば、http://example.com/admin/appearance/update、その後http://example.com/admin/reports/statusを、hook_init()
二回呼び出されます。各ページに1つずつ。
「フックが2回呼び出される」とは、Drupalがブートストラップを完了すると、次のコードを実行するモジュールがあることを意味します。
module_invoke_all('init');
その場合、次のの実装でhook_init()
は同じ値が2回表示されます。
function mymodule_init() {
watchdog('mymodule', 'Request time: !timestamp', array('!timestamp' => REQUEST_TIME), WATCHDOG_DEBUG);
}
REQUEST_TIME
2つの値の違いが2分の場合にコードが表示される場合(例のように)、フックは2回呼び出されませんが、要求されたページごとに1回呼び出されます。
REQUEST_TIME
bootstrap.incで次の行で定義されています。
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
現在リクエストされているページがブラウザに返されない限り、の値はREQUEST_TIME
変化しません。別の値が表示される場合は、別のリクエストページで割り当てられた値を監視しています。