Merge lp:~catch-drupal/pressflow/load_cache into lp:pressflow
- load_cache
- Merge into 6
Status: | Rejected |
---|---|
Rejected by: | David Strauss |
Proposed branch: | lp:~catch-drupal/pressflow/load_cache |
Merge into: | lp:pressflow |
Diff against target: |
821 lines (+739/-1) 8 files modified
modules/node/node.module (+39/-0) modules/node_load_cache/node_load_cache.info (+5/-0) modules/node_load_cache/node_load_cache.install (+40/-0) modules/node_load_cache/node_load_cache.module (+207/-0) modules/user/user.module (+86/-1) modules/user_load_cache/user_load_cache.info (+5/-0) modules/user_load_cache/user_load_cache.install (+31/-0) modules/user_load_cache/user_load_cache.module (+326/-0) |
To merge this branch: | bzr merge lp:~catch-drupal/pressflow/load_cache |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nathaniel Catchpole (community) | Needs Resubmitting | ||
Narayan Newton | Needs Fixing | ||
Review via email: mp+39241@code.launchpad.net |
Commit message
Description of the change
This is a port of the tag1 load_cache.patch to pressflow with a few changes:
# The patches to node and user module follow the same pattern as that used for path.inc/
# Due to this all the caching logic has been moved to node_load_
This has only had light testing and isn't in production yet. However it'll be getting a lot more testing over the next few weeks.
Narayan Newton (nnewton-drupal) wrote : | # |
- 101. By Nathaniel <catch@catch-toshiba>
-
Add default argument for node_load_
cache_user( ).
Narayan Newton (nnewton-drupal) wrote : | # |
Hi Nathan,
I noticed a small issue in the user_save hook which was preventing user_save's from completing on a test site. A patch is below:
=== modified file 'modules/
--- modules/
+++ modules/
@@ -265,10 +265,10 @@
if ($hook_module !== FALSE) {
$function = $hook_module . '_' . $hook;
- $return = $function($account, $array = array(), $category = 'account');
+ $return = $function($account, $array, $category);
}
else {
- $return = _user_save_
+ $return = _user_save_
}
return $return;
}
- 102. By Nathaniel Catchpole <catch@catch-laptop>
-
Fix user_save(), via Narayan.
Nathaniel Catchpole (catch-drupal) wrote : | # |
Thanks Narayan, applied that patch to the branch, resubmitting.
Narayan Newton (nnewton-drupal) wrote : | # |
Hey catch,
Here is a patch to update this to 6.20. Mind looking it over. I didn't push the array rename into the submodule, although we may want to do that.
- 103. By Nathaniel Catchpole <catch@catch-laptop>
-
Always use ->uid, since ['uid'] can be null/0 on login.
- 104. By Nathaniel <catch@catch-toshiba>
-
Merge in trunk.
- 105. By Nathaniel <catch@catch-toshiba>
-
Didn't quite resolve one merge conflict.
- 106. By Nathaniel <catch@catch-toshiba>
-
Set module weight heavier to ensure node_load() cache is cleared after all other node hook implementations run on node_save().
Alex Andrascu (office-visualcandy-eu) wrote : | # |
What's the status of this branch. Anything i should know before giving it a test drive ?
Thanks.
Nathaniel Catchpole (catch-drupal) wrote : | # |
It should be quite stable, however I'm no longer maintaining these branches on launchpad - have moved to github instead at https:/
Alex Andrascu (office-visualcandy-eu) wrote : | # |
Hi Nat,
Thanks alot for letting me know. I've installed it on blottr.com (updating from Pressflow 6.20)
Everything went smooth. User cache and node cache enabled and mapped to their memcached bins.
Looks fine to me. Is there a new version of this one on git ?
Cheers
David Strauss (davidstrauss) wrote : | # |
Just to let folks here know, I've moved Pressflow 6 to GitHub: https:/
Please post any new requested changes as pull requests there for review and integration.
David Strauss (davidstrauss) wrote : | # |
Pressflow 6 is now maintained on GitHub, so I'm marking this merge as rejected.
Unmerged revisions
- 106. By Nathaniel <catch@catch-toshiba>
-
Set module weight heavier to ensure node_load() cache is cleared after all other node hook implementations run on node_save().
- 105. By Nathaniel <catch@catch-toshiba>
-
Didn't quite resolve one merge conflict.
- 104. By Nathaniel <catch@catch-toshiba>
-
Merge in trunk.
- 103. By Nathaniel Catchpole <catch@catch-laptop>
-
Always use ->uid, since ['uid'] can be null/0 on login.
- 102. By Nathaniel Catchpole <catch@catch-laptop>
-
Fix user_save(), via Narayan.
- 101. By Nathaniel <catch@catch-toshiba>
-
Add default argument for node_load_
cache_user( ). - 100. By Nathaniel <catch@catch-toshiba>
-
Clear cache for translations when module is in a translation set.
- 99. By Nathaniel <catch@catch-toshiba>
-
Update from launchpad.
- 98. By Nathaniel <catch@catch-toshiba>
-
Merge in pressflow trunk.
- 97. By Nathaniel <catch@catch-toshiba>
-
Correct function name.
Preview Diff
1 | === modified file 'modules/node/node.module' |
2 | --- modules/node/node.module 2011-05-26 02:58:29 +0000 |
3 | +++ modules/node/node.module 2011-08-10 06:37:33 +0000 |
4 | @@ -687,6 +687,8 @@ |
5 | } |
6 | |
7 | /** |
8 | + * Replacement for node_load() to redirect to an alternative function when available. |
9 | + * |
10 | * Load a node object from the database. |
11 | * |
12 | * @param $param |
13 | @@ -700,6 +702,43 @@ |
14 | * A fully-populated node object. |
15 | */ |
16 | function node_load($param = array(), $revision = NULL, $reset = NULL) { |
17 | + static $hook_module; |
18 | + $hook = 'node_load_cache'; |
19 | + |
20 | + if (!isset($hook_module)) { |
21 | + $modules = module_implements($hook); |
22 | + if (!empty($modules)) { |
23 | + $hook_module = reset($modules); |
24 | + } |
25 | + else { |
26 | + $hook_module = FALSE; |
27 | + } |
28 | + } |
29 | + |
30 | + if ($hook_module !== FALSE) { |
31 | + $function = $hook_module . '_' . $hook; |
32 | + $return = $function($param, $revision, $reset); |
33 | + } |
34 | + else { |
35 | + $return = _node_load_direct($param, $revision, $reset); |
36 | + } |
37 | + return $return; |
38 | +} |
39 | + |
40 | +/** |
41 | + * Load a node object from the database. |
42 | + * |
43 | + * @param $param |
44 | + * Either the nid of the node or an array of conditions to match against in the database query |
45 | + * @param $revision |
46 | + * Which numbered revision to load. Defaults to the current version. |
47 | + * @param $reset |
48 | + * Whether to reset the internal node_load cache. |
49 | + * |
50 | + * @return |
51 | + * A fully-populated node object. |
52 | + */ |
53 | +function _node_load_direct($param = array(), $revision = NULL, $reset = NULL) { |
54 | static $nodes = array(); |
55 | |
56 | if ($reset) { |
57 | |
58 | === added directory 'modules/node_load_cache' |
59 | === added file 'modules/node_load_cache/node_load_cache.info' |
60 | --- modules/node_load_cache/node_load_cache.info 1970-01-01 00:00:00 +0000 |
61 | +++ modules/node_load_cache/node_load_cache.info 2011-08-10 06:37:33 +0000 |
62 | @@ -0,0 +1,5 @@ |
63 | +; $Id$ |
64 | +name = Node load cache |
65 | +description = Peristent caching for nodes. |
66 | +version = VERSION |
67 | +core = 6.x |
68 | |
69 | === added file 'modules/node_load_cache/node_load_cache.install' |
70 | --- modules/node_load_cache/node_load_cache.install 1970-01-01 00:00:00 +0000 |
71 | +++ modules/node_load_cache/node_load_cache.install 2011-08-10 06:37:33 +0000 |
72 | @@ -0,0 +1,40 @@ |
73 | +<?php |
74 | +// $Id |
75 | + |
76 | +/** |
77 | + * @file |
78 | + * Install and update functions for node_load_cache module. |
79 | + */ |
80 | + |
81 | +/** |
82 | + * Implements hook_schema(). |
83 | + */ |
84 | +function node_load_cache_schema() { |
85 | + $schema['cache_node'] = drupal_get_schema_unprocessed('system', 'cache'); |
86 | + $schema['cache_node']['description'] = 'Cache bin for node objects.'; |
87 | + |
88 | + return $schema; |
89 | +} |
90 | + |
91 | +/** |
92 | + * Implements hook_install(). |
93 | + */ |
94 | +function node_load_cache_install() { |
95 | + drupal_install_schema('node_load_cache'); |
96 | + db_query("UPDATE {system} SET weight = 500 WHERE name = 'node_load_cache'"); |
97 | +} |
98 | + |
99 | +/** |
100 | + * Implements hook_uninstall(). |
101 | + */ |
102 | +function node_load_cache_uninstall() { |
103 | + drupal_uninstall_schema('node_load_cache'); |
104 | +} |
105 | + |
106 | +/** |
107 | + * Update module to set a heavier weight. |
108 | + */ |
109 | +function node_load_cache_update_6000() { |
110 | + db_query("UPDATE {system} SET weight = 500 WHERE name = 'node_load_cache'"); |
111 | + return array(); |
112 | +} |
113 | |
114 | === added file 'modules/node_load_cache/node_load_cache.module' |
115 | --- modules/node_load_cache/node_load_cache.module 1970-01-01 00:00:00 +0000 |
116 | +++ modules/node_load_cache/node_load_cache.module 2011-08-10 06:37:33 +0000 |
117 | @@ -0,0 +1,207 @@ |
118 | +<?php |
119 | +// $Id |
120 | + |
121 | +/** |
122 | + * @file |
123 | + * Persistent caching for node_load(). |
124 | + */ |
125 | + |
126 | +/** |
127 | + * Implementation of hook_node_load_cache(). |
128 | + * |
129 | + * Load a node object from the database. |
130 | + * |
131 | + * @param $param |
132 | + * Either the nid of the node or an array of conditions to match against in the database query |
133 | + * @param $revision |
134 | + * Which numbered revision to load. Defaults to the current version. |
135 | + * @param $reset |
136 | + * Whether to reset the internal node_load cache. |
137 | + * |
138 | + * @return |
139 | + * A fully-populated node object. |
140 | + */ |
141 | +function node_load_cache_node_load_cache($param = array(), $revision = NULL, $reset = NULL) { |
142 | + global $user; |
143 | + static $nodes = array(); |
144 | + |
145 | + $cachable = ($revision == NULL && is_numeric($param)) ? "node/$param" : 0; |
146 | + if ($reset) { |
147 | + $nodes = array(); |
148 | + if ($cacheable) { |
149 | + node_load_cache_clear($param); |
150 | + } |
151 | + } |
152 | + |
153 | + $arguments = array(); |
154 | + if (is_numeric($param)) { |
155 | + if ($cachable) { |
156 | + // Is the node statically cached? |
157 | + if (isset($nodes[$param])) { |
158 | + return is_object($nodes[$param]) ? drupal_clone($nodes[$param]) : $nodes[$param]; |
159 | + } |
160 | + $cache = cache_get($cachable, 'cache_node'); |
161 | + if (is_object($cache)) { |
162 | + $cache = $cache->data; |
163 | + // Cache the node statically so we don't have to re-request. |
164 | + $nodes[$param] = is_object($cache) ? drupal_clone($cache) : $cache; |
165 | + // Node types like polls need to do some custom logic when users are |
166 | + // logged in and are more difficult to cache. We still get benefit |
167 | + // from caching these node types for anonymous users. |
168 | + if (!(in_array($nodes[$param]->type, variable_get('cache_node_anonymous_types', array('poll'))) && $user->uid)) { |
169 | + return drupal_clone($nodes[$param]); |
170 | + } |
171 | + } |
172 | + } |
173 | + $cond = 'n.nid = %d'; |
174 | + $arguments[] = $param; |
175 | + } |
176 | + elseif (is_array($param)) { |
177 | + // Turn the conditions into a query. |
178 | + foreach ($param as $key => $value) { |
179 | + $cond[] = 'n.'. db_escape_table($key) ." = '%s'"; |
180 | + $arguments[] = $value; |
181 | + } |
182 | + $cond = implode(' AND ', $cond); |
183 | + } |
184 | + else { |
185 | + return FALSE; |
186 | + } |
187 | + |
188 | + // Retrieve a field list based on the site's schema. |
189 | + $fields = drupal_schema_fields_sql('node', 'n'); |
190 | + $fields = array_merge($fields, drupal_schema_fields_sql('node_revisions', 'r')); |
191 | + $fields = array_merge($fields, array('u.name', 'u.picture', 'u.data')); |
192 | + // Remove fields not needed in the query: n.vid and r.nid are redundant, |
193 | + // n.title is unnecessary because the node title comes from the |
194 | + // node_revisions table. We'll keep r.vid, r.title, and n.nid. |
195 | + $fields = array_diff($fields, array('n.vid', 'n.title', 'r.nid')); |
196 | + $fields = implode(', ', $fields); |
197 | + // Rename timestamp field for clarity. |
198 | + $fields = str_replace('r.timestamp', 'r.timestamp AS revision_timestamp', $fields); |
199 | + // Change name of revision uid so it doesn't conflict with n.uid. |
200 | + $fields = str_replace('r.uid', 'r.uid AS revision_uid', $fields); |
201 | + |
202 | + // Retrieve the node. |
203 | + // No db_rewrite_sql is applied so as to get complete indexing for search. |
204 | + if ($revision) { |
205 | + array_unshift($arguments, $revision); |
206 | + $node = db_fetch_object(db_query('SELECT '. $fields .' FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = %d WHERE '. $cond, $arguments)); |
207 | + } |
208 | + else { |
209 | + $node = db_fetch_object(db_query('SELECT '. $fields .' FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. $cond, $arguments)); |
210 | + } |
211 | + |
212 | + if ($node && $node->nid) { |
213 | + // Call the node specific callback (if any) and piggy-back the |
214 | + // results to the node or overwrite some values. |
215 | + if ($extra = node_invoke($node, 'load')) { |
216 | + foreach ($extra as $key => $value) { |
217 | + $node->$key = $value; |
218 | + } |
219 | + } |
220 | + |
221 | + if ($extra = node_invoke_nodeapi($node, 'load')) { |
222 | + foreach ($extra as $key => $value) { |
223 | + $node->$key = $value; |
224 | + } |
225 | + } |
226 | + if ($cachable) { |
227 | + // To completely exclude a node type from the cache, add it to the |
228 | + // cache_node_exclude_types variable (as an array). To exclude a |
229 | + // node type from the cache only for logged in users, add it to the |
230 | + // cache_node_anyonmous_types variable (as an array). |
231 | + if (!in_array($node->type, variable_get('cache_node_exclude_types', array())) || ($user->uid && !in_array($node->type, variable_get('cache_node_anonymous_types', array('poll'))))) { |
232 | + cache_set($cachable, $node, 'cache_node'); |
233 | + } |
234 | + $nodes[$node->nid] = is_object($node) ? drupal_clone($node) : $node; |
235 | + } |
236 | + } |
237 | + |
238 | + return $node; |
239 | +} |
240 | + |
241 | +/** |
242 | + * Clear the persistent cache for a node. |
243 | + * |
244 | + * @param $nid |
245 | + * The node ID. |
246 | + */ |
247 | +function node_load_cache_clear($nid) { |
248 | + cache_clear_all("node/$nid", 'cache_node'); |
249 | +} |
250 | + |
251 | +/** |
252 | + * Clear nodes that are related to the node being cleared. |
253 | + */ |
254 | +function node_load_cache_clear_related($node) { |
255 | + // Support for translation module. |
256 | + if (module_exists('translation') && translation_supported_type($node->type) && !empty($node->tnid)) { |
257 | + $translations = translation_node_get_translations($node->tnid); |
258 | + foreach ($translations as $translation) { |
259 | + node_load_cache_clear($translation->nid); |
260 | + } |
261 | + } |
262 | +} |
263 | + |
264 | +/** |
265 | + * Implements hook_nodeapi(). |
266 | + */ |
267 | +function node_load_cache_nodeapi(&$node, $op, $a3, $a4) { |
268 | + if ($op == 'update' || $op == 'delete') { |
269 | + node_load_cache_clear($node->nid); |
270 | + node_load_cache_clear_related($node); |
271 | + } |
272 | + if ($op == 'insert') { |
273 | + node_load_cache_clear_related($node); |
274 | + } |
275 | +} |
276 | + |
277 | +/** |
278 | + * Implements hook_comment(). |
279 | + */ |
280 | +function node_load_cache_comment(&$a1, $op) { |
281 | + // Insert and update ops get passed an array. |
282 | + if ($op == 'insert' || $op == 'update') { |
283 | + node_load_cache_clear($a1['nid']); |
284 | + } |
285 | + // Delete op gets an object. |
286 | + if ($op == 'delete') { |
287 | + node_load_cache_clear($a1->nid); |
288 | + } |
289 | +} |
290 | + |
291 | +/** |
292 | + * Implements hook_user(). |
293 | + */ |
294 | +function node_load_cache_user($op, &$edit, &$account, $category = NULL) { |
295 | + // User module adds name, picture and other values to the node object |
296 | + // in hook_node_load(). Clear the node cache when changes are made. |
297 | + if ($op == 'update' || $op == 'delete') { |
298 | + $result = db_query('SELECT nid FROM {node} WHERE uid = %d', $account->uid); |
299 | + while ($record = db_fetch_object($result)) { |
300 | + node_load_cache_clear($record->nid); |
301 | + } |
302 | + } |
303 | +} |
304 | + |
305 | +/** |
306 | + * Implementation of hook_flush_caches(). |
307 | + */ |
308 | +function node_load_cache_flush_caches() { |
309 | + return array('cache_node'); |
310 | +} |
311 | + |
312 | +/** |
313 | + * Implements hook_form_FORM_ID_alter(). |
314 | + */ |
315 | +function node_load_cache_poll_view_voting_form_alter(&$form, &$form_state) { |
316 | + $form['vote']['#submit'][] = '_node_load_cache_poll_vote_submit'; |
317 | +} |
318 | + |
319 | +/** |
320 | + * Clear the node_load() cache when a poll vote is submitted. |
321 | + */ |
322 | +function _node_load_cache_poll_vote_submit($form, &$form_state) { |
323 | + node_load_cache_clear($form['#node']->nid); |
324 | +} |
325 | |
326 | === modified file 'modules/user/user.module' |
327 | --- modules/user/user.module 2011-05-26 02:58:29 +0000 |
328 | +++ modules/user/user.module 2011-08-10 06:37:33 +0000 |
329 | @@ -127,6 +127,46 @@ |
330 | } |
331 | |
332 | /** |
333 | + * Replacement for user_load() to redirect to an alternative implementation when available. |
334 | + * |
335 | + * Fetch a user object. |
336 | + * |
337 | + * @param $array |
338 | + * An associative array of attributes to search for in selecting the |
339 | + * user, such as user name or e-mail address. |
340 | + * |
341 | + * @param $reset |
342 | + * Whether to reset the user_load() cache. |
343 | + * |
344 | + * @return |
345 | + * A fully-loaded $user object upon successful user load or FALSE if user |
346 | + * cannot be loaded. |
347 | + */ |
348 | +function user_load($array = array(), $reset = FALSE) { |
349 | + static $hook_module; |
350 | + $hook = 'user_load_cache_load'; |
351 | + |
352 | + if (!isset($hook_module)) { |
353 | + $modules = module_implements('user_load_cache_load'); |
354 | + if (!empty($modules)) { |
355 | + $hook_module = reset($modules); |
356 | + } |
357 | + else { |
358 | + $hook_module = FALSE; |
359 | + } |
360 | + } |
361 | + |
362 | + if ($hook_module !== FALSE) { |
363 | + $function = $hook_module . '_' . $hook; |
364 | + $return = $function($array, $reset); |
365 | + } |
366 | + else { |
367 | + $return = _user_load_direct($array); |
368 | + } |
369 | + return $return; |
370 | +} |
371 | + |
372 | +/** |
373 | * Fetch a user object. |
374 | * |
375 | * @param $user_info |
376 | @@ -139,7 +179,7 @@ |
377 | * A fully-loaded $user object upon successful user load or FALSE if user |
378 | * cannot be loaded. |
379 | */ |
380 | -function user_load($user_info = array()) { |
381 | +function _user_load_direct($user_info = array()) { |
382 | // Dynamically compose a SQL query: |
383 | $query = array(); |
384 | $params = array(); |
385 | @@ -191,6 +231,8 @@ |
386 | } |
387 | |
388 | /** |
389 | + * Replacement for user_save that redirects to an alternative implementation if available. |
390 | + * |
391 | * Save changes to a user account or add a new user. |
392 | * |
393 | * @param $account |
394 | @@ -209,6 +251,49 @@ |
395 | * A fully-loaded $user object upon successful save or FALSE if the save failed. |
396 | */ |
397 | function user_save($account, $array = array(), $category = 'account') { |
398 | + static $hook_module; |
399 | + $hook = 'user_load_cache_save'; |
400 | + |
401 | + if (!isset($hook_module)) { |
402 | + $modules = module_implements($hook); |
403 | + if (!empty($modules)) { |
404 | + $hook_module = reset($modules); |
405 | + } |
406 | + else { |
407 | + $hook_module = FALSE; |
408 | + } |
409 | + } |
410 | + |
411 | + if ($hook_module !== FALSE) { |
412 | + $function = $hook_module . '_' . $hook; |
413 | + $return = $function($account, $array, $category); |
414 | + } |
415 | + else { |
416 | + $return = _user_save_direct($account, $array, $category); |
417 | + } |
418 | + return $return; |
419 | +} |
420 | + |
421 | + |
422 | +/** |
423 | + * Save changes to a user account or add a new user. |
424 | + * |
425 | + * @param $account |
426 | + * The $user object for the user to modify or add. If $user->uid is |
427 | + * omitted, a new user will be added. |
428 | + * |
429 | + * @param $array |
430 | + * (optional) An array of fields and values to save. For example, |
431 | + * array('name' => 'My name'); Setting a field to NULL deletes it from |
432 | + * the data column. |
433 | + * |
434 | + * @param $category |
435 | + * (optional) The category for storing profile information in. |
436 | + * |
437 | + * @return |
438 | + * A fully-loaded $user object upon successful save or FALSE if the save failed. |
439 | + */ |
440 | +function _user_save_direct($account, $array = array(), $category = 'account') { |
441 | // Dynamically compose a SQL query: |
442 | $user_fields = user_fields(); |
443 | if (is_object($account) && $account->uid) { |
444 | |
445 | === added directory 'modules/user_load_cache' |
446 | === added file 'modules/user_load_cache/user_load_cache.info' |
447 | --- modules/user_load_cache/user_load_cache.info 1970-01-01 00:00:00 +0000 |
448 | +++ modules/user_load_cache/user_load_cache.info 2011-08-10 06:37:33 +0000 |
449 | @@ -0,0 +1,5 @@ |
450 | +; $Id$ |
451 | +name = User load caching |
452 | +description = Static and persistent caching for users. |
453 | +version = VERSION |
454 | +core = 6.x |
455 | |
456 | === added file 'modules/user_load_cache/user_load_cache.install' |
457 | --- modules/user_load_cache/user_load_cache.install 1970-01-01 00:00:00 +0000 |
458 | +++ modules/user_load_cache/user_load_cache.install 2011-08-10 06:37:33 +0000 |
459 | @@ -0,0 +1,31 @@ |
460 | +<?php |
461 | +// $Id |
462 | + |
463 | +/** |
464 | + * @file |
465 | + * Install and update functions for user_load_cache module. |
466 | + */ |
467 | + |
468 | +/** |
469 | + * Implements hook_schema(). |
470 | + */ |
471 | +function user_load_cache_schema() { |
472 | + $schema['cache_user'] = drupal_get_schema_unprocessed('system', 'cache'); |
473 | + $schema['cache_user']['description'] = 'Cache bin for user objects.'; |
474 | + |
475 | + return $schema; |
476 | +} |
477 | + |
478 | +/** |
479 | + * Implements hook_install(). |
480 | + */ |
481 | +function user_load_cache_install() { |
482 | + drupal_install_schema('user_load_cache'); |
483 | +} |
484 | + |
485 | +/** |
486 | + * Implements hook_uninstall(). |
487 | + */ |
488 | +function user_load_cache_uninstall() { |
489 | + drupal_uninstall_schema('user_load_cache'); |
490 | +} |
491 | |
492 | === added file 'modules/user_load_cache/user_load_cache.module' |
493 | --- modules/user_load_cache/user_load_cache.module 1970-01-01 00:00:00 +0000 |
494 | +++ modules/user_load_cache/user_load_cache.module 2011-08-10 06:37:33 +0000 |
495 | @@ -0,0 +1,326 @@ |
496 | +<?php |
497 | +// $Id |
498 | + |
499 | +/** |
500 | + * @file |
501 | + * Add static and persistent caching to user_load(). |
502 | + */ |
503 | + |
504 | +/** |
505 | + * Implementation of hook_user_load_cache_load(). |
506 | + * |
507 | + * Fetch a user object. |
508 | + * |
509 | + * @param $array |
510 | + * An associative array of attributes to search for in selecting the |
511 | + * user, such as user name or e-mail address. |
512 | + * @param $reset |
513 | + * (optional) Resets the static user cache, necessary when the user account |
514 | + * has been saved. |
515 | + * |
516 | + * @return |
517 | + * A fully-loaded $user object upon successful user load or FALSE if user |
518 | + * cannot be loaded. |
519 | + */ |
520 | +function user_load_cache_user_load_cache_load($array = array(), $reset = FALSE) { |
521 | + static $user_cache = array(); |
522 | + // Dynamically compose a SQL query: |
523 | + $query = array(); |
524 | + $params = array(); |
525 | + |
526 | + if (is_numeric($array)) { |
527 | + $array = array('uid' => $array); |
528 | + } |
529 | + elseif (!is_array($array)) { |
530 | + return FALSE; |
531 | + } |
532 | + |
533 | + // Retrieve user from cache |
534 | + if (count($array) === 1 && isset($array['uid']) && $array['uid']) { |
535 | + if (!$reset && isset($user_cache[$array['uid']])) { |
536 | + return $user_cache[$array['uid']]; |
537 | + } |
538 | + $cid = 'user/' . $array['uid']; |
539 | + $cache = cache_get($cid, 'cache_user'); |
540 | + if (is_object($cache)) { |
541 | + $user_cache[$array['uid']] = $cache->data; |
542 | + return $cache->data; |
543 | + } |
544 | + } |
545 | + else { |
546 | + $cid = 0; |
547 | + } |
548 | + |
549 | + foreach ($array as $key => $value) { |
550 | + if ($key == 'uid' || $key == 'status') { |
551 | + $query[] = "$key = %d"; |
552 | + $params[] = $value; |
553 | + } |
554 | + else if ($key == 'pass') { |
555 | + $query[] = "pass = '%s'"; |
556 | + $params[] = md5($value); |
557 | + } |
558 | + else { |
559 | + $query[]= "$key = '%s'"; |
560 | + $params[] = $value; |
561 | + } |
562 | + } |
563 | + $result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params); |
564 | + |
565 | + if ($user = db_fetch_object($result)) { |
566 | + $user = drupal_unpack($user); |
567 | + |
568 | + $user->roles = array(); |
569 | + if ($user->uid) { |
570 | + $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; |
571 | + } |
572 | + else { |
573 | + $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; |
574 | + } |
575 | + $result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d', $user->uid); |
576 | + while ($role = db_fetch_object($result)) { |
577 | + $user->roles[$role->rid] = $role->name; |
578 | + } |
579 | + user_module_invoke('load', $array, $user); |
580 | + } |
581 | + else { |
582 | + $user = FALSE; |
583 | + } |
584 | + |
585 | + if ($user->uid && $cid) { |
586 | + // Add the user to the static cache. |
587 | + $user_cache[$array['uid']] = $user; |
588 | + |
589 | + // Add the user to the persistent cache. |
590 | + cache_set($cid, $user, 'cache_user'); |
591 | + } |
592 | + |
593 | + return $user; |
594 | +} |
595 | + |
596 | +/** |
597 | + * Implementation of hook_user_load_cache_save(). |
598 | + */ |
599 | +function user_load_cache_user_load_cache_save($account, $array = array(), $category = 'account') { |
600 | + // Dynamically compose a SQL query: |
601 | + $user_fields = user_fields(); |
602 | + if (is_object($account) && $account->uid) { |
603 | + $cid = "user/$account->uid"; |
604 | + user_module_invoke('update', $array, $account, $category); |
605 | + $query = ''; |
606 | + $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid))); |
607 | + // Consider users edited by an administrator as logged in, if they haven't |
608 | + // already, so anonymous users can view the profile (if allowed). |
609 | + if (empty($array['access']) && empty($account->access) && user_access('administer users')) { |
610 | + $array['access'] = time(); |
611 | + } |
612 | + foreach ($array as $key => $value) { |
613 | + if ($key == 'pass' && !empty($value)) { |
614 | + $query .= "$key = '%s', "; |
615 | + $v[] = md5($value); |
616 | + } |
617 | + else if ((substr($key, 0, 4) !== 'auth') && ($key != 'pass')) { |
618 | + if (in_array($key, $user_fields)) { |
619 | + // Save standard fields. |
620 | + $query .= "$key = '%s', "; |
621 | + $v[] = $value; |
622 | + } |
623 | + else if ($key != 'roles') { |
624 | + // Roles is a special case: it used below. |
625 | + if ($value === NULL) { |
626 | + unset($data[$key]); |
627 | + } |
628 | + elseif (!empty($key)) { |
629 | + $data[$key] = $value; |
630 | + } |
631 | + } |
632 | + } |
633 | + } |
634 | + $query .= "data = '%s' "; |
635 | + $v[] = serialize($data); |
636 | + |
637 | + $success = db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid))); |
638 | + if (!$success) { |
639 | + // The query failed - better to abort the save than risk further data loss. |
640 | + return FALSE; |
641 | + } |
642 | + |
643 | + // Reload user roles if provided. |
644 | + if (isset($array['roles']) && is_array($array['roles'])) { |
645 | + db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid); |
646 | + |
647 | + foreach (array_keys($array['roles']) as $rid) { |
648 | + if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) { |
649 | + db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid); |
650 | + } |
651 | + } |
652 | + } |
653 | + |
654 | + // Delete a blocked user's sessions to kick them if they are online. |
655 | + if (isset($array['status']) && $array['status'] == 0) { |
656 | + sess_destroy_uid($account->uid); |
657 | + } |
658 | + |
659 | + // If the password changed, delete all open sessions and recreate |
660 | + // the current one. |
661 | + if (!empty($array['pass'])) { |
662 | + sess_destroy_uid($account->uid); |
663 | + if ($account->uid == $GLOBALS['user']->uid) { |
664 | + sess_regenerate(); |
665 | + } |
666 | + } |
667 | + |
668 | + // Flush user from cache. |
669 | + cache_clear_all($cid, 'cache_user'); |
670 | + |
671 | + // Refresh user object. |
672 | + $user = user_load(array('uid' => $account->uid), TRUE); |
673 | + |
674 | + // Send emails after we have the new user object. |
675 | + if (isset($array['status']) && $array['status'] != $account->status) { |
676 | + // The user's status is changing; conditionally send notification email. |
677 | + $op = $array['status'] == 1 ? 'status_activated' : 'status_blocked'; |
678 | + _user_mail_notify($op, $user); |
679 | + } |
680 | + |
681 | + user_module_invoke('after_update', $array, $user, $category); |
682 | + } |
683 | + else { |
684 | + // Allow 'created' to be set by the caller. |
685 | + if (!isset($array['created'])) { |
686 | + $array['created'] = time(); |
687 | + } |
688 | + // Consider users created by an administrator as already logged in, so |
689 | + // anonymous users can view the profile (if allowed). |
690 | + if (empty($array['access']) && user_access('administer users')) { |
691 | + $array['access'] = time(); |
692 | + } |
693 | + |
694 | + // Note: we wait to save the data column to prevent module-handled |
695 | + // fields from being saved there. We cannot invoke hook_user('insert') here |
696 | + // because we don't have a fully initialized user object yet. |
697 | + foreach ($array as $key => $value) { |
698 | + switch ($key) { |
699 | + case 'pass': |
700 | + $fields[] = $key; |
701 | + $values[] = md5($value); |
702 | + $s[] = "'%s'"; |
703 | + break; |
704 | + case 'mode': case 'sort': case 'timezone': |
705 | + case 'threshold': case 'created': case 'access': |
706 | + case 'login': case 'status': |
707 | + $fields[] = $key; |
708 | + $values[] = $value; |
709 | + $s[] = "%d"; |
710 | + break; |
711 | + default: |
712 | + if (substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) { |
713 | + $fields[] = $key; |
714 | + $values[] = $value; |
715 | + $s[] = "'%s'"; |
716 | + } |
717 | + break; |
718 | + } |
719 | + } |
720 | + $success = db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values); |
721 | + if (!$success) { |
722 | + // On a failed INSERT some other existing user's uid may be returned. |
723 | + // We must abort to avoid overwriting their account. |
724 | + return FALSE; |
725 | + } |
726 | + |
727 | + // Build the initial user object. |
728 | + $array['uid'] = db_last_insert_id('users', 'uid'); |
729 | + $user = user_load(array('uid' => $array['uid']), TRUE); |
730 | + |
731 | + $cid = 'user/' . $array['uid']; |
732 | + user_module_invoke('insert', $array, $user, $category); |
733 | + |
734 | + // Build and save the serialized data field now. |
735 | + $data = array(); |
736 | + foreach ($array as $key => $value) { |
737 | + if ((substr($key, 0, 4) !== 'auth') && ($key != 'roles') && (!in_array($key, $user_fields)) && ($value !== NULL)) { |
738 | + $data[$key] = $value; |
739 | + } |
740 | + } |
741 | + db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", serialize($data), $user->uid); |
742 | + |
743 | + // Save user roles (delete just to be safe). |
744 | + if (isset($array['roles']) && is_array($array['roles'])) { |
745 | + db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']); |
746 | + foreach (array_keys($array['roles']) as $rid) { |
747 | + if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) { |
748 | + db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid); |
749 | + } |
750 | + } |
751 | + } |
752 | + |
753 | + // Flush user from cache. |
754 | + cache_clear_all($cid, 'cache_user'); |
755 | + |
756 | + // Build the finished user object. |
757 | + $user = user_load(array('uid' => $array['uid']), TRUE); |
758 | + } |
759 | + |
760 | + // Save distributed authentication mappings. |
761 | + $authmaps = array(); |
762 | + foreach ($array as $key => $value) { |
763 | + if (substr($key, 0, 4) == 'auth') { |
764 | + $authmaps[$key] = $value; |
765 | + } |
766 | + } |
767 | + if (sizeof($authmaps) > 0) { |
768 | + user_set_authmaps($user, $authmaps); |
769 | + } |
770 | + cache_clear_all($cid, 'cache_user'); |
771 | + |
772 | + return $user; |
773 | +} |
774 | + |
775 | + |
776 | +/** |
777 | + * Clear the user_load() cache. |
778 | + * |
779 | + * $param $uid |
780 | + * The user ID. |
781 | + */ |
782 | +function user_load_cache_clear($uid) { |
783 | + cache_clear_all("user/$uid", 'cache_user'); |
784 | + // memcache-session.inc caches the user object too, ensure that gets cleared. |
785 | + cache_clear_all($uid, 'users'); |
786 | +} |
787 | + |
788 | +/** |
789 | + * Implementation of hook_user(). |
790 | + */ |
791 | +function user_load_cache_user($op, &$edit, &$account, $category = NULL) { |
792 | + if ($op == 'update' || $op == 'delete' || $op == 'login') { |
793 | + user_load_cache_clear($account->uid); |
794 | + } |
795 | +} |
796 | + |
797 | +/** |
798 | + * Implementation of hook_flush_caches(). |
799 | + */ |
800 | +function user_load_cache_flush_caches() { |
801 | + return array('cache_user'); |
802 | +} |
803 | + |
804 | +/** |
805 | + * Implementation of hook_form_FORM_ID_alter(). |
806 | + * |
807 | + * @todo: is this necessary? |
808 | + */ |
809 | +function user_load_cache_form_user_profile_form_alter(&$form, &$form_state) { |
810 | + $form['#submit'][] = '_user_load_cache_profile_form_submit'; |
811 | +} |
812 | + |
813 | +/** |
814 | + * Submit handler for the user profile form. |
815 | + */ |
816 | +function _user_load_cache_profile_form_submit($form, &$form_state) { |
817 | + if (user_access('select different theme')) { |
818 | + // This prevents the system theme from getting corrupted. |
819 | + init_theme(); |
820 | + } |
821 | +} |
First pass of this looks good and very similar to what we did with the path alias cache. I was in the process of doing something like this concurrently for another client when I found your branch and am now testing this on the client site.
-N