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 21:54:53 -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);
+  $user = user_load($user->uid);
 
   // 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 21:54:53 -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 21:54:55 -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 21:54:55 -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.
