hook_boot() is D6's version of D5's hook_init(). The API docs say:
This hook is run at the beginning of the page request. ... Only use this hook if your code must run even for cached page views.
However, unless I'm missing something, hook_boot() is not invoked for cached page views. hook_boot() is invoked by drupal_bootstrap():
case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
// Initialize configuration variables, using values from settings.php if available.
$conf = variable_init(isset($conf) ? $conf : array());
_drupal_cache_init($phase);
// Start a page timer:
timer_start('page');
bootstrap_invoke_all('boot');
drupal_page_header();
break;
_drupal_cache_init() is called first. If a page cache hit is found, it never returns:
elseif ($phase == DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE) {
if ($cache = page_get_cache()) {
if (variable_get('cache', CACHE_DISABLED) == CACHE_AGGRESSIVE) {
drupal_page_cache_header($cache);
exit();
}
elseif (variable_get('cache', CACHE_DISABLED) == CACHE_NORMAL) {
require_once './includes/module.inc';
drupal_page_cache_header($cache);
bootstrap_invoke_all('exit');
exit();
}
}
It seems to me that in drupal_bootstrap(DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE), _drupal_cache_init() should be called after timer_start('page') and hook_boot(). Alternatively, hook_boot() should be called before hook_exit() in _drupal_cache_init(). I am not yet 100% sure I understand this situation so I am not ready to attach a patch.
| Comment | File | Size | Author |
|---|---|---|---|
| #15 | bootstrap-reorder_2.patch | 3.92 KB | chx |
| #14 | bootstrap-reorder_1.patch | 3.96 KB | chx |
| #11 | bootstrap-reorder_0.patch | 3.99 KB | chx |
| #10 | hook-boot-182675-10.patch | 1.71 KB | bjaspan |
| #8 | bootstrap-reorder.patch | 825 bytes | chx |
Comments
Comment #1
bjaspan commentedFor reference, this bug was introduced in http://drupal.org/node/165331; the commit is http://cvs.drupal.org/viewvc.py/drupal/drupal/includes/bootstrap.inc?r1=.... I want to study that issue closely to figure out why chx thought the patch was the correct choice before submitting a new patch to undo it.
Comment #2
JirkaRybka commentedI just wanted to point out, that the timer might be a problem too: I haven't got time to research (it's far from being critical), but on my site, since D4.7->D5 upgrade, normal caching kills the page-building-time columns in logs (the last two at admin/logs/pages - Drupal 5) - it's all zeros now, excepting authenticated users activity which shows well. Now reading this issue, I got an impression that this might be the source (unchecked).
Comment #3
hass commentedUndo #165331 isn't a solution...
Comment #4
gábor hojtsyI could imagine chx did not think this through that deeply. I wonder how hass confirmed this working as it does not look like it fires on cached pages, indeed. The hook_boot() call used to be in _drupal_cache_init() but only *before* the normal cache call. Now it is *after* _drupal_cache_init(), so I guess that was not the right solution for the bug. Hass? How did you find that it was working?
Comment #5
chx commentedSo yes, the movement is not perfect to say the least and I think we discussed this in #drupal , aren't we? I have faint memories.
I would move the require_once './includes/module.inc'; to bootstrap before the _drupal_cache_init($phase); :
Comment #6
hass commentedThat's some time ago... can say for sure, but i tried to update global_redirect and it has worked for me as logged in user... but as i remember i haven't tested with cache mode on. Sorry.
Comment #7
JirkaRybka commentedI examined the timer part now... And yes, the
timer_start('page')should go before_drupal_cache_init($phase);, and perhaps even earlier. What I found:-- My site was previously running 4.7.3, where the timer start is called very early (even before banned IP check), and indeed the Statistics page (admin/logs/pages) worked well, showing how much we gain from caching.
-- Then we upgraded to 5.1, where the timer is set after _drupal_cache_init() exactly like in 6.x - and I noticed immediately that something's wrong with Statistics, as all cached pages claimed to be built in zero time. Attaching a screenshot, how silly the page looks (and we have quite a few persons with access to that page, it's not only me). Also note that Drupal says Statistics is only incompatible with Agressive caching, not incompatible with Normal caching.
-- I tried to edit bootstrap.inc, swapping the two calls (timer vs. cache - no 'boot' invoke in 5.x yet), and the Statistics page immediately came to life, average numbers recovering from zero slowly as page visits go.
I know that this might be considered another bug, but it's essentially the same problem as discussed here, and the fix may therefore easily include both. (Tell me if I should open a separate issue for this.)
Comment #8
chx commentedComment #9
JirkaRybka commentedFine with me :)
Which is to say: The patch applies cleanly, and includes the timer fix, exactly as tested on my site. I've no clue how to test the hook_boot part, though.
Comment #10
bjaspan commentedReading the docs:
1. The hook_boot documentation (http://api.drupal.org/api/function/hook_boot/6) says hook_boot is called on every page request. hook_exit (http://api.drupal.org/api/function/hook_exit/6) is similar.
2. The caching mode description at admin/settings/performance says that in Aggressive mode, hook_boot and hook_exit are skipped. This really should be documented in the hook_boot and hook_exit docs, but whatever.
3. The behavior of the 'page' timer does not appear to be documented. However, I observe that among at least core and the devel.module, it is only used in hook_exit which means it is useless in Aggressive caching mode. However, starting a timer is a low-cost operation and there is no real reason not to do it. Also, it ought to be started as early as possible.
Based on these docs, the patch in #8 is incorrect. It calls hook_boot on every page request but does not call hook_exit on page requests in aggressive cache mode. I've attached a new patch which:
1. Calls hook_boot and hook_exit on cache hits in normal mode but not aggressive mode. Calling hook_boot on normal cache hits is the actual fix to the bug I initially reported.
2. Moves timer_start('page') at the first bootstrap level; it's a tiny little function.
3. Calls hook_boot for non-cache-hit requests. The code already did this.
I tested the hook_boot behavior in disabled, normal, and aggressive cache mode using Persistent Login which actually depends on it (i.e. in aggressive mode, Persistent Login should fail to work).
Honestly, I think Aggressive cache mode should be removed. If a module depends on hook_boot and hook_exit, and you don't want to incur that overhead, don't use the module! This feature has no real value, makes the code confusing, and clutters the interface. But that is an issue for a separate patch.
Comment #11
chx commentedI have removed _drupal_cache_init as after removing the require_once there was zero common code for the two calls of this function. This speeds up the code as require_once is slow, so now there is only one instead of two (not a big gain, but why not). I have fixed common.inc not using the cache mode constant.
Comment #12
chx commentedAbout aggressive caching, sometimes you can live without the functionality in hook_boot/exit. (I am better in writing patches than explaining them.)
Comment #13
JirkaRybka commented#11 is incorrect in the timer part! timer_start() uses a global variable, so it needs to be moved one line down, just after the security code in drupal_unset_globals() is called - otherwise the global variable will get destroyed if 'register_globals' is ON, and timer will no more work.
(Right now I have no patch-rolling environment here, sorry.)
Comment #14
chx commentedComment #15
chx commentedI called cache_exit always if the cache was enabled in bootstrap. This is now fixed.
Comment #16
moshe weitzman commentedI gave this patch a thorough test with devel and various cache configurations. It worked as advertised. helped me find a subtle devel bug as well.
Comment #17
gábor hojtsyGreat. I looked over this code very carefully. Noticed that what we save here is that previously a page_get_cache() call was there on all page views, now this is only called when the cache is enabled, but the module.inc file is always included in this stage, as it is required at multiple places. So that's what's traded here, additionally to the fixes made. Committed the patch.
Comment #18
(not verified) commentedAutomatically closed -- issue fixed for two weeks with no activity.
Comment #19
greg.harveyHi guys,
Thought I'd re-open this, as the issue appears to be identical. It seems this fix may have been unfixed? I'm using Drupal 6.6. I started with a
hook_init()implementation in my module, but then realised I needed my code to execute on every page, regardless of cache, so I switched tohook_boot(), cleared caches to be sure, but same effect. Finally I triedhook_exit()and got the same behaviour again - not being invoked in caches pages.To test, I went from Normal caching to no caching and everything was fine. It seems this bug has snuck back in and is also affecting
hook_exit(). =(For reference, installed contributed modules are all up to date, as is Drupal core. NO patches have been applied. Enabled list is as follows, so you can see I have no weird modules installed:
- CCK 2.1 (entire package)
- Email field 1.1
- Link field 2.5
- Date 2.0-rc4
- Date API 2.0-rc4
- Date Copy 2.0-rc4
- Date Timezone 2.0-rc4
- Node Images 2.x-dev
- Code filter 1.0
- Drupaler (my module, just a hook_form_alter to add tab order to CAPTCHA on the user login field)
- Google Analytics 1.2
- Node comment 1.2-rc2
- Pathauto 1.1
- Token 1.11
- CAPTCHA 1.0-rc2
- Text CAPTCHA 1.0-rc2
- Views 2.1
- Views UI 2.1
Core:
- Blog
- Database logging
- Help
- Menu
- Path
- PHP Filter
- Search
- Taxonomy
- Upload
Comment #20
dave reidhook_exit was fixed in #318102: hook_exit() not invoked for some cached requests. The followup for hook_boot not being called is in #323474: hook_boot() not invoked on uncached page views if cache mode is AGGRESSIVE. Lets leave this fixed and if there are any problems, post in the followup issue.
Comment #21
greg.harveyOk, I'll post another issue - btw, the hook_exit() fix was never explicitly stated as applied to D6 in the thread you reference. Has it been applied do you know? I could ask there, but I'll end up confusing matters I fear!
Comment #22
dave reidYes it was committed by Gabor (see CVS commit at http://drupal.org/cvs?commit=146776) and was included in the 6.6 release.