diff -r e04e6a6dbb51 modules/user/user.module
--- a/modules/user/user.module	Sun Nov 02 21:04:34 2008 +0100
+++ b/modules/user/user.module	Sun Nov 02 21:10:44 2008 +0100
@@ -136,29 +136,86 @@ function user_external_login($account, $
 
 /**
  * Fetch a user object.
  *
- * @param $array
+ * @param $conditions
  *   An associative array of attributes to search for in selecting the
  *   user, such as user name or e-mail address.
  *
+ * @param $reset
+ *   Resets the internal cache for the first user object that is saved
+ *   in the cache that matches the $conditions, or when $conditions is
+ *   an empty array it resets the whole internal cache
+ *
  * @return
  *   A fully-loaded $user object upon successful user load or FALSE if user
- *   cannot be loaded.
+ *   cannot be loaded. When $conditions is empty and $reset is TRUE a 
+ *   FALSE boolean is returned.
  */
-function user_load($array = array()) {
-  // Dynamically compose a SQL query:
-  $query = array();
-  $params = array();
+function user_load($conditions = array(), $reset = NULL) {
+  static $cache;
 
-  if (is_numeric($array)) {
-    $array = array('uid' => $array);
+  // Instantiate cache or reset cache when conditions are zero.
+  if (!$cache || (empty($conditions) && $reset)) {
+    $cache = array();
+    $cache['users'] = $cache['conditions'] = array();
+
+    if (empty($conditions)) {
+      return FALSE;
+    }
   }
-  elseif (!is_array($array)) {
+
+  // Make sure $conditions is an array at all times or return FALSE.
+  if (is_numeric($conditions)) {
+    $conditions = array('uid' => $conditions);
+  }
+  elseif (!is_array($conditions)) {
     return FALSE;
   }
 
-  foreach ($array as $key => $value) {
+  // Return user from cache if only uid was given.
+  if (isset($conditions['uid']) && (count($conditions)==1) && !$reset) {
+    $uid = $conditions['uid'];
+    if (isset($cache['users'][$uid])) {
+      return $cache['users'][$uid];
+    }
+  }
+
+  // Try to match the exact condition set and return from cache.
+  $uid_matches = array();
+  foreach ($conditions as $key => $value) {
+    if ($key == 'uid') continue;
+    if (isset($cache['conditions'][$key], $cache['conditions'][$key][$value])) {
+
+      // Collect the matching uid's.
+      foreach ($cache['conditions'][$key][$value] as $uid) {
+        $uid_matches[] = (int)$uid;
+      }
+    }
+    else {
+      break;
+    }
+  }
+
+  if ($match_count = count($uid_matches)) {
+    if ((array_sum($uid_matches) / $match_count) == $uid_matches[0]) { // Are all uid's in the array equal?
+      $uid = $uid_matches[0];
+
+      if (isset($cache['users'][$uid])) {
+        if ($reset) {
+          unset($cache['users'][$uid]);
+        }
+        else {
+          return $cache['users'][$uid];
+        }
+      }
+    }
+  }
+
+  // Dynamically compose a SQL query.
+  $query = $params = array();
+
+  foreach ($conditions as $key => $value) {
     if ($key == 'uid' || $key == 'status') {
       $query[] = "$key = %d";
       $params[] = $value;
     }
@@ -170,12 +227,12 @@ function user_load($array = array()) {
       $query[]= "LOWER($key) = LOWER('%s')";
       $params[] = $value;
     }
   }
-  $result = db_query('SELECT * FROM {users} u WHERE ' . implode(' AND ', $query), $params);
+  $result = db_fetch_object(db_query('SELECT * FROM {users} u WHERE ' . implode(' AND ', $query), $params));
 
-  if ($user = db_fetch_object($result)) {
-    $user = drupal_unpack($user);
+  if (isset($result->uid)) {
+    $user = drupal_unpack($result);
 
     $user->roles = array();
     if ($user->uid) {
       $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
@@ -186,11 +243,32 @@ function user_load($array = array()) {
     $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;
     }
-    user_module_invoke('load', $array, $user);
+    user_module_invoke('load', $conditions, $user);
+
+    // Save the uid of the object in the conditions cache,
+    // we group the database keys, then their values so we
+    // have a unique match at all times.
+    foreach ($conditions as $key => $value) {
+      if (!isset($cache['conditions'][$key][$value])) {
+        $cache['conditions'][$key][$value] = array();
+      }
+      if (!in_array($user->uid, $cache['conditions'][$key][$value])) {
+        $cache['conditions'][$key][$value][] = $user->uid;
+      }
+    }
+
+    // Save user object in cache.
+    $cache['users'][$user->uid] = $user;
   }
   else {
+    if (isset($conditions['uid'])) {
+      $uid = $conditions['uid'];
+      if (isset($cache['users'][$uid])) {
+        unset($cache['users'][$uid]);
+      }
+    }
     $user = FALSE;
   }
 
   return $user;
@@ -287,9 +365,9 @@ function user_save($account, $edit = arr
       drupal_session_regenerate();
     }
 
     // Refresh user object.
-    $user = user_load(array('uid' => $account->uid));
+    $user = user_load($edit['uid'], TRUE);
 
     // Send emails after we have the new user object.
     if (isset($edit['status']) && $edit['status'] != $account->status) {
       // The user's status is changing; conditionally send notification email.
@@ -317,9 +395,9 @@ function user_save($account, $edit = arr
       return FALSE;
     }
 
     // Build the initial user object.
-    $user = user_load(array('uid' => $edit['uid']));
+    $user = user_load(array('uid' => $edit['uid']), TRUE);
 
     user_module_invoke('insert', $edit, $user, $category);
 
     // Note, we wait with saving the data column to prevent module-handled
@@ -345,9 +423,9 @@ 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;
 }
@@ -1593,8 +1671,9 @@ function user_delete($edit, $uid) {
   module_invoke_all('user_delete', $edit, $account);
   db_query('DELETE FROM {users} WHERE uid = %d', $uid);
   db_query('DELETE FROM {users_roles} WHERE uid = %d', $uid);
   db_query('DELETE FROM {authmap} WHERE uid = %d', $uid);
+  user_load($uid, TRUE); // Update user_load()'s cache.
   $variables = array('%name' => $account->name, '%email' => '<' . $account->mail . '>');
   watchdog('user', 'Deleted user: %name %email.', $variables, WATCHDOG_NOTICE);
 }
 
@@ -2240,8 +2319,9 @@ function user_block_user_action(&$object
     global $user;
     $uid = $user->uid;
   }
   db_query("UPDATE {users} SET status = 0 WHERE uid = %d", $uid);
+  user_load((int)$uid, TRUE); // Refresh user_load()'s static cache.
   drupal_session_destroy_uid($uid);
   watchdog('action', 'Blocked user %name.', array('%name' => check_plain($user->name)));
 }
 
diff -r e04e6a6dbb51 modules/user/user.test
--- a/modules/user/user.test	Sun Nov 02 21:04:34 2008 +0100
+++ b/modules/user/user.test	Sun Nov 02 21:10:44 2008 +0100
@@ -1,7 +1,10 @@
 <?php
 // $Id: user.test,v 1.17 2008/10/10 07:49:49 webchick Exp $
 
+/**
+ * Test user registration
+ */
 class UserRegistrationTestCase extends DrupalWebTestCase {
   /**
    * Implementation of getInfo().
    */
@@ -76,9 +79,9 @@ 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.
     $this->clickLink(t('Log out'));
@@ -101,9 +104,11 @@ class UserRegistrationTestCase extends D
     $this->assertText(t('Edit'), t('[user auth] Found edit tab on the profile page.'));
   }
 }
 
-
+/**
+ * Verify that username/email validity checks behave as designed.
+ */
 class UserValidationTestCase extends DrupalWebTestCase {
   /**
    * Implementation of getInfo().
    */
@@ -156,9 +161,11 @@ class UserValidationTestCase extends Dru
     }
   }
 }
 
-
+/**
+ * Registers a user and deletes it.
+ */
 class UserDeleteTestCase extends DrupalWebTestCase {
   /**
    * Implementation of getInfo().
    */
@@ -197,12 +204,15 @@ class UserDeleteTestCase extends DrupalW
 
     // Confirm deletion.
     $this->drupalPost(NULL, NULL, t('Delete'));
     $this->assertRaw(t('%name has been deleted.', array('%name' => $user->name)), t('User deleted'));
-    $this->assertFalse(user_load($edit), t('User is not found in the database'));
+    $this->assertFalse(user_load($edit, TRUE), t('User is not found in the database'));
   }
 }
 
+/**
+ * Checks that the image picture code works as designed.
+ */
 class UserPictureTestCase extends DrupalWebTestCase {
   protected $user;
   protected $_directory_test;
 
@@ -416,9 +426,11 @@ class UserPictureTestCase extends Drupal
     return $pic_path;
   }
 }
 
-
+/**
+ * Verify that role permissions can be added and removed via the permissions page.
+ */
 class UserPermissionsTestCase extends DrupalWebTestCase {
   protected $admin_user;
   protected $rid;
 
@@ -470,8 +482,11 @@ class UserPermissionsTestCase extends Dr
   }
 
 }
 
+/**
+ * Test user admininstration page functionality.
+ */
 class UserAdminTestCase extends DrupalWebTestCase {
   /**
    * Implementation of getInfo().
    */
@@ -518,18 +533,17 @@ class UserAdminTestCase extends DrupalWe
     $edit = array();
     $edit['operation'] = 'block';
     $edit['accounts['. $account->uid .']'] = TRUE;
     $this->drupalPost('admin/user/user', $edit, t('Update'));
-    $account = user_load(array('name' => $user_b->name));
+    $account = user_load(array('name' => $user_b->name), TRUE);
     $this->assertEqual($account->status, 0, 'User B blocked');
   }
 }
 
 /**
  * Test user autocompletion.
  */
 class UserAutocompleteTestCase extends DrupalWebTestCase {
-
   /**
    * Implementation of getInfo().
    */
   function getInfo() {
@@ -569,4 +583,88 @@ class UserAutocompleteTestCase extends D
     // Using first letter of the user's name, make sure the user's full name is in the results.
     $this->assertRaw($this->unprivileged_user->name, t('User name found in autocompletion results.'));
   }
 }
+
+/**
+ * Test loading of users and test the static cache of user_load().
+ */
+class UserLoadingTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('User loading'),
+      'description' => t('Registers a user, loads it in several circumstances and multiple times. Validates that the static cache in the user_load() method works properly.'),
+      'group' => t('User')
+    );
+  }
+  
+  /**
+   * Implementation of setUp()
+   */
+  function setUp() {
+    parent::setUp();
+    $this->user = $this->drupalCreateUser();
+  }
+
+  /**
+   * Test simple user loading by uid.
+   */
+  function testUserLoadByUid() {
+    user_load(array(), TRUE); // Reset static cache.
+    $user = user_load($this->user->uid);
+    $this->assertTrue(isset($user->uid), t("User loaded by uid: user_load(!uid);", array('!uid' => $this->user->uid)) );
+
+    user_load(array(), TRUE); // Reset static cache.
+    $user = user_load(array('uid' => $this->user->uid));
+    $this->assertTrue(isset($user->uid), t("User loaded by uid: user_load(array('uid' => !uid));", array('!uid' => $this->user->uid)) );
+  }
+
+  /**
+   * Test simple user loading by name.
+   */
+  function testUserLoadByName() {
+    user_load(array(), TRUE); // Reset static cache.
+    $user = user_load(array('name' => $this->user->name));
+    $this->assertTrue(isset($user->uid), t("User loaded by name: user_load('name' => '!name');", array('!name' => $this->user->name)) );
+  }
+
+  /**
+   * Test simple user loading by mail address.
+   */
+  function testUserLoadByMail() {
+    user_load(array(), TRUE); // Reset static cache.
+    $user = user_load(array('mail' => $this->user->mail));
+    $this->assertTrue(isset($user->uid), t("User loaded by mail: user_load('mail' => '!mail');", array('!mail' => $this->user->mail)) );
+  }
+
+  /**
+   * Test simple user_load() cache by requesting the
+   * same user and changing the database in between.
+   */
+  function testUserSimpleCache() {
+    $user = user_load($this->user->uid);
+    
+    $fields = array('name' => 'wrongname');
+    db_update('users')->fields($fields)->condition('uid', $user->uid)->execute();
+    
+    $user2 = user_load($this->user->uid);
+    $this->assertEqual($user->name, $user2->name, t("The user object got statically cached."));
+  }
+
+  /**
+   * Test simple user_load() cache refreshing by requesting
+   * the same user and changing the database in between.
+   */
+  function testUserRefreshSimpleCache() {
+    $user = user_load($this->user->uid);
+
+    $fields = array('name' => 'wrongname');
+    db_update('users')->fields($fields)->condition('uid', $user->uid)->execute();
+
+    $user2 = user_load($this->user->uid, TRUE);
+    $this->assertEqual($user2->name, 'wrongname', t("Static user_load() cache got refreshed correctly."));
+  }
+
+}
\ No newline at end of file
