Index: modules/profile/profile.module =================================================================== RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v retrieving revision 1.247 diff -u -p -r1.247 profile.module --- modules/profile/profile.module 5 Dec 2008 12:50:28 -0000 1.247 +++ modules/profile/profile.module 15 Dec 2008 17:02:43 -0000 @@ -194,13 +194,6 @@ function profile_block($op = 'list', $de } /** - * Implementation of hook_user_load(). - */ -function profile_user_load(&$edit, &$user, $category = NULL) { - return profile_load_profile($user); -} - -/** * Implementation of hook_user_register(). */ function profile_user_register(&$edit, &$user, $category = NULL) { @@ -256,11 +249,14 @@ function profile_user_delete(&$edit, &$u db_query('DELETE FROM {profile_value} WHERE uid = %d', $user->uid); } -function profile_load_profile(&$user) { - $result = db_query('SELECT f.name, f.type, v.value FROM {profile_field} f INNER JOIN {profile_value} v ON f.fid = v.fid WHERE uid = %d', $user->uid); - while ($field = db_fetch_object($result)) { - if (empty($user->{$field->name})) { - $user->{$field->name} = _profile_field_serialize($field->type) ? unserialize($field->value) : $field->value; +/** + * Implementation of hook_user_load(). + */ +function profile_user_load($users) { + $result = db_query('SELECT f.name, f.type, v.uid, v.value FROM {profile_field} f INNER JOIN {profile_value} v ON f.fid = v.fid WHERE uid IN (' . db_placeholders(array_keys($users)) . ')', array_keys($users)); + foreach ($result as $record) { + if (empty($users[$record->uid]->{$record->name})) { + $users[$record->uid]->{$record->name} = _profile_field_serialize($record->type) ? unserialize($record->value) : $record->value; } } } @@ -327,7 +323,7 @@ function profile_view_field($user, $fiel function profile_view_profile(&$user) { - profile_load_profile($user); + profile_user_load(array($user->uid, $user)); // Show private fields to administrators and people viewing their own account. if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) { Index: modules/profile/profile.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/profile/profile.pages.inc,v retrieving revision 1.10 diff -u -p -r1.10 profile.pages.inc --- modules/profile/profile.pages.inc 5 Dec 2008 12:50:28 -0000 1.10 +++ modules/profile/profile.pages.inc 15 Dec 2008 17:02:43 -0000 @@ -55,11 +55,13 @@ function profile_browse() { } // Extract the affected users: - $result = pager_query("SELECT u.uid, u.access FROM {users} u INNER JOIN {profile_value} v ON u.uid = v.uid WHERE v.fid = %d AND $query AND u.access != 0 AND u.status != 0 ORDER BY u.access DESC", 20, 0, NULL, $arguments); + $result = pager_query("SELECT u.uid, u.access FROM {users} u INNER JOIN {profile_value} v ON u.uid = v.uid WHERE v.fid = %d AND $query AND u.access != 0 AND u.status != 0 ORDER BY u.access DESC", 20, 0, NULL, $arguments)->fetchAllAssoc('uid'); + + // Load the users. + $users = user_load_multiple(array_keys($result)); $content = ''; - while ($account = db_fetch_object($result)) { - $account = user_load(array('uid' => $account->uid)); + foreach ($users as $account) { $profile = _profile_update_user_fields($fields, $account); $content .= theme('profile_listing', $account, $profile); } @@ -88,11 +90,10 @@ function profile_browse() { } // Extract the affected users: - $result = pager_query('SELECT uid, access FROM {users} WHERE uid > 0 AND status != 0 AND access != 0 ORDER BY access DESC', 20, 0, NULL); - + $result = pager_query('SELECT uid, access FROM {users} WHERE uid > 0 AND status != 0 AND access != 0 ORDER BY access DESC', 20, 0, NULL)->fetchAllAssoc('uid'); + $users = user_load_multiple(array_keys($result)); $content = ''; - while ($account = db_fetch_object($result)) { - $account = user_load(array('uid' => $account->uid)); + foreach ($users as $account) { $profile = _profile_update_user_fields($fields, $account); $content .= theme('profile_listing', $account, $profile); } Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.944 diff -u -p -r1.944 user.module --- modules/user/user.module 29 Nov 2008 09:33:51 -0000 1.944 +++ modules/user/user.module 15 Dec 2008 17:02:44 -0000 @@ -133,65 +133,163 @@ function user_external_login($account, $ } /** - * Fetch a user object. + * Load multiple users based on certain conditions. * - * @param $array - * An associative array of attributes to search for in selecting the - * user, such as user name or e-mail address. + * This function should be used whenever you need to load more than one user + * from the database. Users are loaded into memory and will not require + * database access if loaded again during the same page request. + * + * @param $uids + * An array of user IDs. + * @param $conditions + * An array of conditions to add to the query. + * @param $reset + * Whether to reset the internal cache. * * @return - * A fully-loaded $user object upon successful user load or FALSE if user - * cannot be loaded. + * An array of user objects, indexed by uid. */ -function user_load($array = array()) { - // Dynamically compose a SQL query: - $query = array(); - $params = array(); - - if (is_numeric($array)) { - $array = array('uid' => $array); - } - elseif (!is_array($array)) { - return FALSE; - } - - foreach ($array as $key => $value) { - if ($key == 'uid' || $key == 'status') { - $query[] = "$key = %d"; - $params[] = $value; - } - elseif ($key == 'pass') { - $query[] = "pass = '%s'"; - $params[] = $value; +function user_load_multiple($uids = array(), $conditions = array(), $reset = FALSE) { + static $user_cache = array(); + if ($reset) { + $user_cache = array(); + } + + $users = array(); + + // Create a new variable which is either a prepared version of the $uids + // array for later comparison with the user cache, or FALSE if no $uids were + // passed. The $uids array is reduced as items are loaded from cache, and we + // need to know if it's empty for this reason to avoid querying the database + // when all requested users are loaded from cache. + $passed_uids = !empty($uids) ? array_flip($uids) : FALSE; + + // Load any available users from the internal cache. + if ($user_cache) { + if ($uids) { + $users += array_intersect_key($user_cache, $passed_uids); + // If any users were loaded, remove them from the $uids still to load. + $uids = array_keys(array_diff_key($passed_uids, $users)); } - else { - $query[]= "LOWER($key) = LOWER('%s')"; - $params[] = $value; + // If only conditions is passed, load all users from the cache. Users + // which don't match conditions will be removed later. + elseif ($conditions) { + $users = $user_cache; } } - $result = db_query('SELECT * FROM {users} u WHERE ' . implode(' AND ', $query), $params); - if ($user = db_fetch_object($result)) { - $user = drupal_unpack($user); + // Remove any loaded users from the array if they don't match $conditions. + if ($conditions) { + foreach ($users as $user) { + $user_values = (array) $user; + if (isset($conditions['name']) && strcasecmp($conditions['name'], $user_values['name'] !== 0)) { + unset($users[$user->uid]); + } + if (isset($conditions['mail']) && strcasecmp($conditions['mail'], $user_values['mail'] !== 0)) { + unset($users[$user->uid]); + } + elseif (array_diff_assoc($conditions, $user_values)) { + unset($users[$user->uid]); + } + } + } - $user->roles = array(); - if ($user->uid) { - $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; + // Load any remaining users from the database, this is necessary if we have + // $uids still to load, or if $conditions was passed without $uids. + if ($uids || ($conditions && !$passed_uids)) { + $query = db_select('users', 'u'); + $user_fields = drupal_schema_fields_sql('users'); + $query->fields('u', $user_fields); + + // If the $uids array is populated, add those to the query. + if ($uids) { + $query->condition('u.uid', $uids, 'IN'); } - else { - $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; + // If the conditions array is populated, add those to the query. + if ($conditions) { + if (isset($conditions['name'])) { + $query->condition('u.name', $conditions['name'], 'LIKE'); + unset($conditions['name']); + } + if (isset($conditions['mail'])) { + $query->condition('u.mail', $conditions['mail'], 'LIKE'); + unset($conditions['mail']); + } + foreach ($conditions as $field => $value) { + $query->conditions('u.' . $field, $value); + } } - $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); - while ($role = db_fetch_object($result)) { - $user->roles[$role->rid] = $role->name; + $result = $query->execute(); + + $queried_users = array(); + foreach ($result as $record) { + $queried_users[$record->uid] = drupal_unpack($record); + $queried_users[$record->uid]->roles = array(); + if ($record->uid) { + $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; + } + else { + $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; + } + } + + if (!empty($queried_users)) { + // Add any additional roles from the database. + $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (' . db_placeholders(array_keys($queried_users)) . ')', array_keys($queried_users)); + foreach ($result as $record) { + $queried_users[$record->uid]->roles[$record->rid] = $record->name; + } + + // Invoke hook_user_load() on the users loaded from the database + // and add them to the static cache. + foreach (module_implements('user_load') as $module) { + $function = $module . '_user_load'; + $function($queried_users); + } + $users += $queried_users; + $user_cache += $queried_users; } - user_module_invoke('load', $array, $user); } - else { - $user = FALSE; + + // Ensure that the returned array is ordered the same as the original $uids + // array if this was passed in and remove any invalid uids. + if ($passed_uids) { + // Remove any invalid uids from the array. + $passed_uids = array_intersect_key($passed_uids, $users); + foreach ($users as $user) { + $passed_uids[$user->uid] = $user; + } + $users = $passed_uids; } - return $user; + return $users; +} + + +/** + * Fetch a user object. + * + * @param $conditions + * An associative array of attributes to search for in selecting the + * user, such as user name or e-mail address. + * + * @return + * A fully-loaded $user object upon successful user load or FALSE if user + * cannot be loaded. + */ +function user_load($conditions, $reset = FALSE) { + $uids = array(); + + if (is_numeric($conditions)) { + $uids[] = $conditions; + $conditions = array(); + } + elseif (isset($conditions['uid'])) { + $uids = array($conditions['uid']); + unset($conditions['uid']); + } + $users = user_load_multiple($uids, $conditions, $reset); + return reset($users); } /** @@ -286,7 +384,7 @@ function user_save($account, $edit = arr } // Refresh user object. - $user = user_load(array('uid' => $account->uid)); + $user = user_load(array('uid' => $account->uid), TRUE); // Send emails after we have the new user object. if (isset($edit['status']) && $edit['status'] != $account->status) { @@ -344,7 +442,7 @@ function user_save($account, $edit = arr } // Build the finished user object. - $user = user_load(array('uid' => $edit['uid'])); + $user = user_load(array('uid' => $edit['uid']), TRUE); } return $user; @@ -1576,7 +1674,7 @@ function _user_edit_submit($uid, &$edit) * @param $uid The user ID of the user to delete. */ function user_delete($edit, $uid) { - $account = user_load(array('uid' => $uid)); + $account = user_load($uid); drupal_session_destroy_uid($uid); _user_mail_notify('status_deleted', $account); module_invoke_all('user_delete', $edit, $account); Index: modules/user/user.test =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.test,v retrieving revision 1.22 diff -u -p -r1.22 user.test --- modules/user/user.test 25 Nov 2008 13:14:29 -0000 1.22 +++ modules/user/user.test 15 Dec 2008 17:02:45 -0000 @@ -74,7 +74,7 @@ class UserRegistrationTestCase extends D // Make sure password changes are present in database. require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - $user = user_load(array('uid' => $user->uid)); + $user = user_load(array('uid' => $user->uid), TRUE); $this->assertTrue(user_check_password($new_pass, $user), t('Correct password in database.')); // Logout of user account.