As the title says, user_load(0) gets called on every page when not logged in (clean install). According to this comment by chx, user_load() should not be called with the anonymous user id, so this can be considered a bug.
Adding the PHP5 function debug_print_backtrace() to user_load() gave me this result (arguments trimmed down for readability):
#0 user_load(0) called at [/Users/joakim/Sites/drupal/modules/user/user.module:1112]
#1 user_current_load(0) called at [/Users/joakim/Sites/drupal/includes/menu.inc:405]
#2 _menu_load_objects(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:630]
#3 _menu_link_translate(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:942]
#4 _menu_tree_check_access(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:931]
#5 menu_tree_check_access(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:887]
#6 menu_tree_page_data(navigation) called at [/Users/joakim/Sites/drupal/includes/menu.inc:686]
#7 menu_tree() called at [/Users/joakim/Sites/drupal/modules/user/user.module:732]
#8 user_block(view, 1) called at [(null):0]
#9 call_user_func_array(user_block, Array ([2] => view,[3] => 1)) called at [/Users/joakim/Sites/drupal/includes/module.inc:450]
#10 module_invoke(user, block, view, 1) called at [/Users/joakim/Sites/drupal/modules/block/block.module:455]
#11 block_list(left) called at [/Users/joakim/Sites/drupal/includes/theme.inc:1515]
#12 theme_blocks(left) called at [(null):0]
#13 call_user_func_array(theme_blocks, Array ([0] => left)) called at [/Users/joakim/Sites/drupal/includes/theme.inc:591]
#14 theme(blocks, left) called at [/Users/joakim/Sites/drupal/includes/theme.inc:1709]
#15 template_preprocess_page(...) called at [(null):0]
#16 call_user_func_array(template_preprocess_page, ...) called at [/Users/joakim/Sites/drupal/includes/theme.inc:632]
#17 theme(page, ...) called at [/Users/joakim/Sites/drupal/index.php:36]
With user.module's blocks disabled (User login and Navigation), menu_tree_page_data(navigation) will still get called from menu_set_active_trail() (arguments trimmed):
#0 user_load(0) called at [/Users/joakim/Sites/drupal/modules/user/user.module:1112]
#1 user_current_load(0) called at [/Users/joakim/Sites/drupal/includes/menu.inc:405]
#2 _menu_load_objects(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:630]
#3 _menu_link_translate(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:942]
#4 _menu_tree_check_access(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:931]
#5 menu_tree_check_access(...) called at [/Users/joakim/Sites/drupal/includes/menu.inc:887]
#6 menu_tree_page_data(navigation) called at [/Users/joakim/Sites/drupal/includes/menu.inc:1449]
#7 menu_set_active_trail() called at [/Users/joakim/Sites/drupal/includes/menu.inc:1481]
#8 menu_get_active_trail() called at [/Users/joakim/Sites/drupal/includes/menu.inc:1491]
#9 menu_get_active_breadcrumb() called at [/Users/joakim/Sites/drupal/includes/common.inc:93]
#10 drupal_get_breadcrumb() called at [/Users/joakim/Sites/drupal/includes/theme.inc:1745]
#11 template_preprocess_page(...) called at [(null):0]
#12 call_user_func_array(template_preprocess_page, ...) called at [/Users/joakim/Sites/drupal/includes/theme.inc:632]
#13 theme(page, ...) called at [/Users/joakim/Sites/drupal/index.php:36]
One quick and easy way to fix this, is to in user_load_current() check if the current uid is 0, and if it is pass the anonymous user object with drupal_anonymous_user() instead of calling user_load(). See attached patch.
And I'm curious, why can't user_load() also deal with the anonymous uid? A simple if clause can return the anonymous user object before any db queries are made. I guess there's a perfectly good reason for this, but I would like to know what it is :)
| Comment | File | Size | Author |
|---|---|---|---|
| #6 | user_load_backtrace.patch | 571 bytes | ximo |
| #4 | user_load_anon.patch | 688 bytes | ximo |
| #1 | user_load_anonymous_uid_2.patch | 913 bytes | ximo |
| user_load_anonymous_uid.patch | 715 bytes | ximo |
Comments
Comment #1
ximo commenteduser_load_self()also callsuser_load($GLOBALS['user']->uid), which is bad practice considering the user may be anonymous. I've ammended a fix for this to the previous patch..BTW, the function is never used in core and it's not documented. Leftover from an earlier version?
Comment #2
gábor hojtsyWhy not return drupal_anonymous_user() in user load() (which avoid DB requests for user_load()), so all callers are covered?
Comment #3
ximo commentedDuh :) I was hooked up on not using user_load() for anon user, forgot to think about all contrib modules who might be doing it wrong.
isset($array['uid'])is because user_load() is called with no uid during installation.Comment #4
ximo commentedSorry, I forgot to attach the new patch. Time to take my coffee.
Edit: Seems the file didn't want to upload, not my fault :) Trying again...
Comment #5
killes@www.drop.org commentedThe title is no longer true. Actualy, user_current_load is no longer called from any place in core.
I am changing the title.
The patch in #4 should IMO not be applied, we try to not check every argument in a function, we try to educate the caller.
Comment #6
ximo commentedAccording to the backtrace, user_current_load() is called programmatically by _menu_load_objects() in menu.inc (HEAD), so the old title was still valid. See the patch below to review the backtrace on your install. The function will also get called for anonymous users, which as I understand it, shouldn't happen.
I agree with your point on educating the caller though. _menu_load_objects() is the only caller in core AFAIK. And some documentation would be helpful for user_current_load() and user_load_self().
Comment #7
mr.baileysIn D7, user_load_self has been removed. Even in D6, I don't think there is a genuine need for that function.