When loading the currently logged-in user, user_load() should add the session properties (from the sessions table) to the $user object.
This is not necessarily a bug in core per se but leads to bugginess if/when a contrib module reloads the global $user object via user_load().
The issue is that some properties, such as $user->cache, are missing from $user object constructed by user_load() that are normally available in the $user object constructed by _sess_read(). This results in e.g. a PHP notice for undefined property $user->cache when calling cache_get().
See also #489478: og module unsets $user->cache.
Comment | File | Size | Author |
---|---|---|---|
#40 | drupal.user-load.40.patch | 7.35 KB | sun |
#38 | drupal.user-load.37.patch | 7.67 KB | sun |
#29 | drupal.user-load.29.patch | 8 KB | sun |
#26 | drupal.user-load.26.patch | 7.91 KB | sun |
#22 | drupal.user-load.22.patch | 7.91 KB | sun |
Comments
Comment #1
mfbComment #2
mfbThis was arguably more of a bug in Organic Groups which has been fixed over in #489478: og module unsets $user->cache: og_init() now adds its extra data into the global $user rather than replacing the global $user.
Comment #3
moshe weitzman CreditAttribution: moshe weitzman commentedWithout this code, it is easy to neuter the $user object just by reloading it (i.e. user_load()). For example, the $user->cache property disappears and thats a key part of cache::get
Comment #5
mfbThis might be a bogus test failure?
Comment #7
andypostComment #9
Amazon CreditAttribution: Amazon commentedbad bot.
Comment #11
mfbComment #12
Kars-T CreditAttribution: Kars-T commentedWanted to review this but HEAD moved very far. It was hard to track down as entity_load is now used but the UserController Class does use the exact code and did miss this patch. So I reissue your patch against the latest head. My test so far run fine with the patch. Maybe the user.test should add some tests for those fields?
Comment #14
Kars-T CreditAttribution: Kars-T commentedwrong path...
Comment #15
moshe weitzman CreditAttribution: moshe weitzman commentedThanks for reroll. Back to RTBC.
Comment #16
webchick1. Let's get a test showing the previous buggy behaviour to ensure user_load()s continue to not blow away 'core' information in the future.
2. Should we name-space these properties in some way ($user->session->XXX perhaps) so that it's clear that sid is "session ID" and not "slushie ID"?
Comment #17
moshe weitzman CreditAttribution: moshe weitzman commentedReworked the patch so that we pull session info from the DB instead of from the existing user object. Thats what we do for roles already, so I think it is simplest to be consistent. This adds one (very fast) query to user_load().
1. I added a test which fails for current HEAD but passes with this patch. Ergo, we shouldn't ever break this.
2. Agreed, but for D8. This patch really isn't about moving properties, it is assuring that they are always present.
This patch is a prerequisite for #361471: Global $user object should be a complete entity
Comment #18
sunHmmm... we already have a session_test.module. Not sure whether we need to introduce user_test.module for this issue. (we definitely need to do it at some point, but all of this rather maps to testing session handling IMHO)
This comment seems to be stale? At least the rest of the patch doesn't seem to pass any $_GET parameters.
Should state what the test module function does, not what the test does.
"Menu callback; Output the current user's session id."
Superfluous PHPDoc line.
Missing trailing period (full-stop).
If we would simply use a DIV id="session_id" here, having the user's session id as text content, then we would simply use xpath() to retrieve the value in the test.
"Re-attach session data for the current user."
Since we are still in user_load() and generating the value that will be re-assigned to $user, can't we simply copy over the values from $user to $record->uid instead of querying the db?
Trailing white-space here.
s/case to test//
This review is powered by Dreditor.
Comment #19
sunI now see your reasoning for querying the database.... however, catch really tried hard to cut down the database queries recently and succeeded doing so. Following this direction, I guess we should not run a query and just copy over.
Comment #20
catchSince session.inc is pluggable the data may not be in session table, so yeah copying over seems right here.
Comment #21
moshe weitzman CreditAttribution: moshe weitzman commentedImplemented all feedback including copying of properties and using session_test module.
Comment #22
sunI wonder whether we shouldn't move the copying of user object values into session.inc - hence, we don't have to consult the schema, and furthermore, an alternative session handling could do whatever it likes. I.e. we simply call the function
drupal_session_user_load($record)
.In addition, it seems like we could remove the drupal_unpack() + user roles handling from http://api.drupal.org/api/function/_drupal_session_read/7 with this patch? Or should code be able to expect that $user->roles is defined prior full bootstrap? I wouldn't necessarily expect that. At the very least, I don't think that anything needs $user->data unpacked before full bootstrap.
Attached patch contains what I have in mind. The previous patch implemented the conditional copying of $user properties at the wrong place, and I can see why that happened, so I additionally cleaned up the UserController::attachLoad() function.
Since that would fail without the change from #361471: Global $user object should be a complete entity, I've incorporated those two lines in here. (errr, and while doing so, also fixed those two lines ;)
Thoughts?
Comment #23
catchThat still seems to be doing a direct query, it looks like the only way we can get this according to _drupal_session_read() is to copy over from $_SESSION.
Comment #24
sunhuh? Which query?
Or do you mean the schema "query"?
I'm on crack. Are you, too?
Comment #25
catchahh, I missed what was going on in Moshe's patch, and crossposted with yours which makes it more obvious. The approach seems fine in itself, but note there's an 8% performance improvement over at #638070: Router loaders causing a lot of database hits for access checks and we don't have the Drupal 6 problem of no static caching of user_load(). So while in principle I dislike the fake $user object we get, it's a pattern we've had for several releases, and while it might have been a false optimization before, it's not now.
Comment #26
sunAlso note that this patch removes 1 query from all requests that don't do a full bootstrap.
Fixed the tests.
Comment #27
moshe weitzman CreditAttribution: moshe weitzman commentedThanks for the enhancements. Looking good. Removing that query for all requests, even cached ones, is a pretty big win. It makes all permission checks before full bootstrap invalid. Some hook_boot() implementations might not be happy about that. Hmmm.
How about "Add session specific properties to $user account object."
" ...can be populated."
no comma needed
The browser based test below also does an account reload. Perhaps only do the role test on the current user and not the reload test. Or do the role test in the session callback also? Current way seems like it duplicates.
== operator instead of =
Comment #28
catchWe don't start a session for anonymous users until we have to, so for most cached pages that query shouldn't be called (although I'm not 100% what the status is for that), and if you put sessions elsewhere, then it goes anyway. I'm sticking to #25.
Comment #29
sunI equally tried to think of any code/implementation that would want or require access to $user->roles before full bootstrap, but couldn't think of any. At least, core doesn't need it, and if a module does, then it's still able to query that info on its own... Note that this also affects user_access() during bootstrap - but equally, I'm not sure whether there's anything that tries to perform access checks before full bootstrap.
catch is right that this probably only affects uncached pages for authenticated users. But also in that scenario, we're retrieving user roles two times currently, and needlessly unpack {users}.data for the half-baked $user object.
Getting rid of the half-baked $user object in full bootstrap is a huge win, IMHO. I know that I at least ran into this inconsistency issue a couple of times and was all over confused again why $user didn't contain what I expected.
Incorporated moshe's suggestions.
Comment #30
moshe weitzman CreditAttribution: moshe weitzman commentedGood point that core does not start a session until is needed. However, I think that most sites in the wild will have $_SESSION populated by one module or another and will be starting sessions even for anon. It will be performance sensitive sites that look into this and avoid those modules or patch them. So, I think the removed query will help a lot of sites in the real world.
Comment #31
moshe weitzman CreditAttribution: moshe weitzman commentedFor that matter, I think most sites will load the user object on uncached pages so avoiding that like we have discussed in the router items issue is not worth it, IMO. @catch makes some good points about how we have user object static caching in D7 but even then I think it makes sense to just provide a fully baked user object. Few sites/pages will actually get the performance win that a half baked user object provides. IMO, this is a good patch.
Comment #32
moshe weitzman CreditAttribution: moshe weitzman commentedI don't think 'unprocessed' is correct here. We do take schema_alter into account.
This is a tiny function, and is fully explained by the doxygen. I suggest omitting this inline comment.
Comment #36
catchIf we want to remove the double query for user_roles(), we could move that to a helper function with a static cache on $uid and try to use it in both places before hitting the database again.
Comment #38
sunSo the installer fails due to the removed $user->roles before full bootstrap or what? Let's have the testbot a crack at this.
Comment #40
sunUpdated with latest compromise of #361471: Global $user object should be a complete entity
Comment #42
moshe weitzman CreditAttribution: moshe weitzman commentedAnyone willing to refresh this?
Comment #43
Sree CreditAttribution: Sree commentedany further updates on this?
Comment #44
catchLooking back at this I'm not sure why we do this at all. We should just access $_SESSION instead of copying over. If this is still a valid bug in D7 then the schema call here needs to be removed to avoid loading the entire schema cache every request.
Comment #45
moshe weitzman CreditAttribution: moshe weitzman commentedi'm happy with with just accessing $_SESSION
Comment #46
andypostis this still valid for 8.x?
Comment #47
BerdirI think we're working on removing those special properties from the current user object, this goes in the opposite direction. Won't fix?