Refactor session handling by wrapping all session handling function in a DrupalSession object, and publishing it as $_SESSION.

From: Damien Tournoud <damien@tournoud.net>


---

 includes/bootstrap.inc                       |   11 
 includes/cache.inc                           |    4 
 includes/common.inc                          |    9 
 includes/file.inc                            |    8 
 includes/form.inc                            |    6 
 includes/language.inc                        |    2 
 includes/locale.inc                          |    2 
 includes/session.inc                         |  580 ++++++++++++++++----------
 includes/theme.inc                           |    7 
 install.php                                  |    2 
 modules/block/block.module                   |    6 
 modules/blog/blog.module                     |    2 
 modules/blog/blog.pages.inc                  |    4 
 modules/blogapi/blogapi.module               |    2 
 modules/comment/comment.module               |   20 -
 modules/comment/comment.pages.inc            |    2 
 modules/contact/contact.module               |    2 
 modules/contact/contact.pages.inc            |   11 
 modules/dblog/dblog.admin.inc                |    5 
 modules/filter/filter.module                 |    2 
 modules/forum/forum.module                   |   11 
 modules/node/node.module                     |   14 -
 modules/node/node.pages.inc                  |    8 
 modules/php/php.module                       |    2 
 modules/poll/poll.module                     |   10 
 modules/profile/profile.module               |    2 
 modules/simpletest/drupal_web_test_case.php  |    2 
 modules/simpletest/tests/file.test           |   10 
 modules/simpletest/tests/session.test        |   10 
 modules/simpletest/tests/session_test.module |    2 
 modules/statistics/statistics.module         |    2 
 modules/system/system.api.php                |    2 
 modules/system/system.module                 |   10 
 modules/tracker/tracker.module               |    2 
 modules/trigger/trigger.test                 |    2 
 modules/upload/upload.module                 |    4 
 modules/user/user.admin.inc                  |    4 
 modules/user/user.module                     |   91 +---
 modules/user/user.pages.inc                  |   32 -
 39 files changed, 507 insertions(+), 400 deletions(-)


diff --git includes/bootstrap.inc includes/bootstrap.inc
index 7e49d32..0c8727f 100644
--- includes/bootstrap.inc
+++ includes/bootstrap.inc
@@ -658,7 +658,8 @@ function variable_del($name) {
  * a redirected form submission which was completed).
  */
 function page_get_cache() {
-  global $user, $base_root;
+  global $base_root;
+  $user = $_SESSION->user;
 
   $cache = NULL;
 
@@ -883,7 +884,8 @@ function request_uri() {
  * @see watchdog_severity_levels()
  */
 function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
-  global $user, $base_root;
+  global $base_root;
+  $user = $_SESSION->user;
 
   static $in_error_state = FALSE;
 
@@ -1120,8 +1122,7 @@ function _drupal_bootstrap($phase) {
 
     case DRUPAL_BOOTSTRAP_SESSION:
       require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc');
-      session_set_save_handler('_sess_open', '_sess_close', '_sess_read', '_sess_write', '_sess_destroy_sid', '_sess_gc');
-      session_start();
+      DrupalSession::init();
       break;
 
     case DRUPAL_BOOTSTRAP_VARIABLES:
@@ -1199,7 +1200,7 @@ function get_t() {
  *  Choose a language for the current page, based on site and user preferences.
  */
 function drupal_init_language() {
-  global $language, $user;
+  global $language;
 
   // Ensure the language is correctly returned, even without multilanguage support.
   // Useful for eg. XML/HTML 'lang' attributes.
diff --git includes/cache.inc includes/cache.inc
index 108bd2a..8c82ede 100644
--- includes/cache.inc
+++ includes/cache.inc
@@ -15,7 +15,7 @@
  * @return The cache or FALSE on failure.
  */
 function cache_get($cid, $table = 'cache') {
-  global $user;
+  $user = $_SESSION->user;
 
   // Garbage collection necessary when enforcing a minimum cache lifetime
   $cache_flush = variable_get('cache_flush', 0);
@@ -145,7 +145,7 @@ function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $he
  *   match. If '*' is given as $cid, the table $table will be emptied.
  */
 function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
-  global $user;
+  $user = $_SESSION->user;
 
   if (!isset($cid) && !isset($table)) {
     // Clear the block cache first, so stale data will
diff --git includes/common.inc includes/common.inc
index b9efee2..3f8a96e 100644
--- includes/common.inc
+++ includes/common.inc
@@ -341,7 +341,7 @@ function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response
 
   // Even though session_write_close() is registered as a shutdown function, we
   // need all session data written to the database before redirecting.
-  session_write_close();
+  $_SESSION->close();
 
   header('Location: ' . $url, TRUE, $http_response_code);
 
@@ -1462,7 +1462,7 @@ function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
 function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
   static $timezones = array();
   if (!isset($timezone)) {
-    global $user;
+    $user = $_SESSION->user;
     if (variable_get('configurable_timezones', 1) && $user->uid && $user->timezone) {
       $timezone = $user->timezone;
     }
@@ -2829,7 +2829,7 @@ function drupal_get_token($value = '') {
  *   is true, the return value will always be true for anonymous users.
  */
 function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
-  global $user;
+  $user = $_SESSION->user;
   return (($skip_anonymous && $user->uid == 0) || ($token == md5(session_id() . $value . variable_get('drupal_private_key', ''))));
 }
 
@@ -2912,7 +2912,8 @@ function _drupal_bootstrap_full() {
  * @see drupal_page_header
  */
 function page_set_cache() {
-  global $user, $base_root;
+  global $base_root;
+  $user = $_SESSION->user;
 
   if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_get_messages(NULL, FALSE)) == 0) {
     // This will fail in some cases, see page_get_cache() for the explanation.
diff --git includes/file.inc includes/file.inc
index a69d9c6..0e68e15 100644
--- includes/file.inc
+++ includes/file.inc
@@ -800,7 +800,7 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) {
  *   error.
  */
 function file_save_upload($source, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
-  global $user;
+  $user = $_SESSION->user;
   static $upload_cache;
 
   // Return cached objects without processing since the file will have
@@ -974,7 +974,7 @@ function file_validate_name_length($file) {
  * @see hook_file_validate()
  */
 function file_validate_extensions($file, $extensions) {
-  global $user;
+  $user = $_SESSION->user;
 
   $errors = array();
 
@@ -1005,7 +1005,7 @@ function file_validate_extensions($file, $extensions) {
  * @see hook_file_validate()
  */
 function file_validate_size($file, $file_limit = 0, $user_limit = 0) {
-  global $user;
+  $user = $_SESSION->user;
 
   $errors = array();
 
@@ -1124,7 +1124,7 @@ function file_validate_image_resolution(&$file, $maximum_dimensions = 0, $minimu
  * @see file_unmanaged_save_data()
  */
 function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
-  global $user;
+  $user = $_SESSION->user;
 
   if ($filepath = file_unmanaged_save_data($data, $destination, $replace)) {
     // Create a file object.
diff --git includes/form.inc includes/form.inc
index 2de5fc7..f48b3f8 100644
--- includes/form.inc
+++ includes/form.inc
@@ -221,7 +221,7 @@ function drupal_rebuild_form($form_id, &$form_state, $args, $form_build_id = NUL
 function form_get_cache($form_build_id, &$form_state) {
   if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
     $form = $cached->data;
-    global $user;
+    $user = $_SESSION->user;
     if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
       if ($cached = cache_get('storage_' . $form_build_id, 'cache_form')) {
         $form_state['storage'] = $cached->data;
@@ -237,7 +237,7 @@ function form_get_cache($form_build_id, &$form_state) {
 function form_set_cache($form_build_id, $form, $form_state) {
   // 6 hours cache life time for forms should be plenty.
   $expire = 21600;
-  global $user;
+  $user = $_SESSION->user;
   if ($user->uid) {
     $form['#cache_token'] = drupal_get_token();
   }
@@ -462,7 +462,7 @@ function drupal_process_form($form_id, &$form, &$form_state) {
  *   in here so that hook_form_alter() calls can use it, as well.
  */
 function drupal_prepare_form($form_id, &$form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
 
   $form['#type'] = 'form';
   $form['#programmed'] = isset($form['#post']);
diff --git includes/language.inc includes/language.inc
index 9494683..c45c5a9 100644
--- includes/language.inc
+++ includes/language.inc
@@ -10,7 +10,7 @@
  *  Choose a language for the page, based on language negotiation settings.
  */
 function language_initialize() {
-  global $user;
+  $user = $_SESSION->user;
 
   // Configured presentation language mode.
   $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE);
diff --git includes/locale.inc includes/locale.inc
index d019836..c361fff 100644
--- includes/locale.inc
+++ includes/locale.inc
@@ -1751,7 +1751,7 @@ function _locale_export_get_strings($language = NULL, $group = 'default') {
  *   are provided for PO and POT files.
  */
 function _locale_export_po_generate($language = NULL, $strings = array(), $header = NULL) {
-  global $user;
+  $user = $_SESSION->user;
 
   if (!isset($header)) {
     if (isset($language)) {
diff --git includes/session.inc includes/session.inc
index 4f65b7f..9796095 100644
--- includes/session.inc
+++ includes/session.inc
@@ -3,176 +3,393 @@
 
 /**
  * @file
- * User session handling functions.
- *
- * The user-level session storage handlers:
- * - _sess_open()
- * - _sess_close()
- * - _sess_read()
- * - _sess_write()
- * - _sess_destroy_sid()
- * - _sess_gc()
- * are assigned by session_set_save_handler() in bootstrap.inc and are called
- * automatically by PHP. These functions should not be called directly. Session
- * data should instead be accessed via the $_SESSION superglobal.
+ * Session handling features.
  */
 
 /**
- * Session handler assigned by session_set_save_handler().
- *
- * This function is used to handle any initialization, such as file paths or
- * database connections, that is needed before accessing session data. Drupal
- * does not need to initialize anything in this function.
- *
- * This function should not be called directly.
- *
- * @return
- *   This function will always return TRUE.
+ * Provides session handling and validation.
  */
-function _sess_open() {
-  return TRUE;
-}
+class DrupalSession extends ArrayIterator {
+  /**
+   * The original uid of the current user, as retrieved and set in
+   * Session::read(), is stored in this private member so that we can verify
+   * against it during session writing and cleanup.
+   *
+   * @var int
+   */
+  private $uid;
 
-/**
- * Session handler assigned by session_set_save_handler().
- *
- * This function is used to close the current session. Because Drupal stores
- * session data in the database immediately on write, this function does
- * not need to do anything.
- *
- * This function should not be called directly.
- *
- * @return
- *   This function will always return TRUE.
- */
-function _sess_close() {
-  return TRUE;
-}
+  /**
+   * The currently logged in user.
+   *
+   * The currently logged in user, previously the $user = $_SESSION->user, is now attached
+   * directly to the global session object.
+   *
+   * @var stdClass
+   */
+  public $user;
 
-/**
- * Session handler assigned by session_set_save_handler().
- *
- * This function will be called by PHP to retrieve the current user's
- * session data, which is stored in the database. It also loads the
- * current user's appropriate roles into the user object.
- *
- * This function should not be called directly. Session data should
- * instead be accessed via the $_SESSION superglobal.
- *
- * @param $key
- *   Session ID.
- * @return
- *   Either an array of the session data, or an empty string, if no data
- *   was found or the user is anonymous.
- */
-function _sess_read($key) {
-  global $user;
-
-  // Write and Close handlers are called after destructing objects
-  // since PHP 5.0.5.
-  // Thus destructors can use sessions but session handler can't use objects.
-  // So we are moving session closure before destructing objects.
-  register_shutdown_function('session_write_close');
-
-  // Handle the case of first time visitors and clients that don't store
-  // cookies (eg. web crawlers).
-  if (!isset($_COOKIE[session_name()])) {
-    $user = drupal_anonymous_user();
-    return '';
+  /**
+   * Prevent writing the session (useful for test purposes).
+   *
+   * @var boolean
+   */
+  public $preventWrite = FALSE;
+
+  /**
+   * Build the session object.
+   *
+   * The user-level session storage handlers:
+   * - DrupalSession::open()
+   * - DrupalSession::close()
+   * - DrupalSession::read()
+   * - DrupalSession::write()
+   * - DrupalSession::destroy()
+   * - DrupalSession::gc()
+   * are assigned by session_set_save_handler() in bootstrap.inc and are called
+   * automatically by PHP. These functions should not be called directly. Session
+   * data should instead be accessed via the $_SESSION superglobal.
+   */
+  public function __construct() {
+    session_set_save_handler(
+      array(&$this, "openHandler"),
+      array(&$this, "closeHandler"),
+      array(&$this, "readHandler"),
+      array(&$this, "writeHandler"),
+      array(&$this, "destroyHandler"),
+      array(&$this, "gcHandler")
+    );
+    session_start();
+    parent::__construct($_SESSION);
+  }
+
+  /**
+   * Initialize session handling.
+   *
+   * We wrap $_SESSION to our DrupalSession object, so that we can control 
+   * what happens during the lifetime of the session.
+   */
+  public static function init() {
+    $_SESSION = new DrupalSession();
   }
 
-  // Otherwise, if the session is still active, we have a record of the
-  // client's session in the database.
-  $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $key))->fetchObject();
+  /**
+   * Finalize the login process. Must be called when logging in a user.
+   *
+   * The method checks the account object that is to replace the current user,
+   * records a watchdog message about the new session, saves the login
+   * timestamp, calls hook_user op 'login' and generates a new session.
+   *
+   * @param $account
+   *   The new user object intended to replace the currently logged in user.
+   * @param $edit
+   *   This array is passed to hook_user op login.
+   * @return
+   *   Returns the new current user object.
+   */
+  public function userLogin($account, &$edit) {
+    // Set the provided account as the new current user.
+    $this->user = $account;
+    // Set the protected uid record to the new authorized uid.
+    $this->uid = $this->user->uid;
+
+    watchdog('user', 'Session opened for %name.', array('%name' => $this->user->name));
+    // Update the user table timestamp noting user has logged in.
+    // This is also used to invalidate one-time login links.
+    $this->user->login = REQUEST_TIME;
+    db_update('users')
+      ->fields(array(
+        'login' => $this->user->login
+      ))
+      ->condition('uid', $this->user->uid)
+      ->execute();
 
-  // We found the client's session record and they are an authenticated user.
-  if ($user && $user->uid > 0) {
-    // This is done to unserialize the data member of $user.
-    $user = drupal_unpack($user);
+    // Regenerate the session ID to prevent against session fixation attacks.
+    // This is called before hook_user in case one of those functions fails
+    // or incorrectly does a redirect which would leave the old session in place.
+    $this->regenerate();
 
-    // Add roles element to $user.
-    $user->roles = array();
-    $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
-    $user->roles += db_query("SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = :uid", array(':uid' => $user->uid))->fetchAllKeyed(0, 1);
+    user_module_invoke('login', $edit, $this->user);
+    return $this->user;
   }
-  // We didn't find the client's record (session has expired), or they
-  // are an anonymous user.
-  else {
-    $session = isset($user->session) ? $user->session : '';
-    $user = drupal_anonymous_user($session);
+
+  /**
+   * Logout the current user.
+   */
+  public function userLogout() {
+    watchdog('user', 'Session closed for %name.', array('%name' => $this->user->name));
+    // Destroy the current user's session.
+    session_destroy();
+    $this->regenerate();
+
+    module_invoke_all('user_logout', NULL, $this->user);
+    // Load the anonymous user into the current user.
+    $this->user = drupal_anonymous_user();
+    $this->uid = 0;
   }
 
-  return $user->session;
-}
+  /**
+   * Remove all sessions associated with the given uid from the sessions table.
+   *
+   * Note that this method does NOT affect the session associated with the
+   * current page request, as it does not invoke any of PHP's native session
+   * handling functions. Even though the session data is deleted here, it will
+   * be restored at the end of the page request in Session::write().
+   * Consequently, there are two major use cases for calling this method:
+   *  - A user has changed their password, and so all saved sessiosn should be
+   *    destroyed for security reasons. @see user_save()
+   *  - A user is being deleted. @see user_delete()
+   *  - A user has been blocked, and so all saved sessions should be destroyed
+   *    to ensure that they cannot resume a saved session when they later return
+   *    to the site, and that the block is immediately effective (i.e., if they
+   *    are currently browsing, will take effect as of the next page request
+   *    they make). @see user_block_user_action()
+   *
+   * @param int $uid
+   *   The user's uid whose sessions are to be removed.
+   * @return bool
+   *   Returns TRUE to indicate success; always returns this since there are no
+   *   conditions under which it should fail.
+   */
+  public static function destroyByUid($uid) {
+    db_delete('sessions')
+      ->condition('uid', $uid)
+      ->execute();
+    return TRUE;
+  }
 
-/**
- * Session handler assigned by session_set_save_handler().
- *
- * This function will be called by PHP to store the current user's
- * session, which Drupal saves to the database.
- *
- * This function should not be called directly. Session data should
- * instead be accessed via the $_SESSION superglobal.
- *
- * @param $key
- *   Session ID.
- * @param $value
- *   Serialized array of the session data.
- * @return
- *   This function will always return TRUE.
- */
-function _sess_write($key, $value) {
-  global $user;
-
-  // If saving of session data is disabled or if the client doesn't have a session,
-  // and one isn't being created ($value), do nothing. This keeps crawlers out of
-  // the session table. This reduces memory and server load, and gives more useful
-  // statistics. We can't eliminate anonymous session table rows without breaking
-  // the "Who's Online" block.
-  if (!drupal_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) {
+  /**
+   * Close the current session.
+   *
+   * @return bool
+   *   Indicates whether or not the session was successfully closed. The only
+   *   time this should be false is if the function is called when
+   */
+  public function close() {
+    $_SESSION = $this->getArrayCopy();
+    session_write_close();
+    $_SESSION = $this;
     return TRUE;
   }
 
-  db_merge('sessions')
-    ->key(array('sid' => $key))
-    ->fields(array(
-      'uid' => $user->uid,
-      'cache' => isset($user->cache) ? $user->cache : 0,
-      'hostname' => ip_address(),
-      'session' => $value,
-      'timestamp' => REQUEST_TIME,
-    ))
-    ->execute();
-
-  // Last access time is updated no more frequently than once every 180 seconds.
-  // This reduces contention in the users table.
-  if ($user->uid && REQUEST_TIME - $user->access > variable_get('session_write_interval', 180)) {
-    db_update('users')
+  /**
+   * Regenerate the current session.
+   */
+  public function regenerate() {
+    $old_session_id = session_id();
+    // This will define $lifetime, $path, $domain, $secure used below.
+    extract(session_get_cookie_params());
+    // Set "httponly" to TRUE to reduce the risk of session stealing via XSS.
+    session_set_cookie_params($lifetime, $path, $domain, $secure, TRUE);
+    session_regenerate_id();
+    db_update('sessions')
       ->fields(array(
-        'access' => REQUEST_TIME
+        'sid' => session_id()
       ))
-      ->condition('uid', $user->uid)
+      ->condition('sid', $old_session_id)
       ->execute();
+    return TRUE;
   }
 
-  return TRUE;
-}
+  /**
+   * 'Open' session handler assigned by session_set_save_handler().
+   *
+   * This method is used to handle any initialization, such as file paths or
+   * database connections, that is needed before accessing session data. Drupal
+   * does not need to initialize anything in this function.
+   *
+   * @return
+   *   This method will always return TRUE.
+   */
+  protected function openHandler() {
+    return TRUE;
+  }
 
-/**
- * Called when an anonymous user becomes authenticated or vice-versa.
- */
-function drupal_session_regenerate() {
-  $old_session_id = session_id();
-  extract(session_get_cookie_params());
-  // Set "httponly" to TRUE to reduce the risk of session stealing via XSS.
-  session_set_cookie_params($lifetime, $path, $domain, $secure, TRUE);
-  session_regenerate_id();
-  db_update('sessions')
-    ->fields(array(
-      'sid' => session_id()
-    ))
-    ->condition('sid', $old_session_id)
-    ->execute();
+  /**
+   * 'Close' session handler assigned by session_set_save_handler().
+   *
+   * This method is used to close the current session. Because Drupal stores
+   * session data in the database immediately on write, this function does
+   * not need to do anything.
+   *
+   * @return
+   *   This method will always return TRUE.
+   */
+  protected function closeHandler() {
+    return TRUE;
+  }
+
+  /**
+   * 'Read' session handler assigned by session_set_save_handler().
+   *
+   * This method will be called internally by PHP to retrieve the current user's
+   * session data, which is stored in the database. It also loads the
+   * current user's appropriate roles into the user object.
+   *
+   * @param string $key
+   *   Session ID; exactly the same string value as what session_id() returns.
+   * @return mixed
+   *   If no session data was found or if the user is anonymous, an empty string
+   *   is returned. Otherwise, a special serialized string is returned that the
+   *   PHP engine interprets into our $_SESSION array. The form of the string is
+   *   governed by the session.serialize_handler variable in php.ini.
+   *
+   */
+  protected function readHandler($sid) {
+    // Handle clients who present no cookies. These could be first-time visitors,
+    // regular visitors whose session data has just been purged (e.g., by
+    // logging out), or clients that don't store cookies (e.g., web crawlers).
+    if (!isset($_COOKIE[session_name()])) {
+      $this->user = drupal_anonymous_user();
+      return '';
+    }
+
+    // Otherwise, if the session is still active, we have a record of the
+    // client's session in the database.
+    $this->user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $sid))->fetchObject();
+
+    // We found the client's session record and they are an authenticated user.
+    if ($this->user && $this->user->uid > 0) {
+      // This is done to unserialize the data member of $user.
+      $this->user = drupal_unpack($this->user);
+
+      // Add roles element to $this->user.
+      $this->user->roles = array();
+      $this->user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+      $this->user->roles += db_query("SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = :uid", array(':uid' => $this->user->uid))->fetchAllKeyed(0, 1);
+    }
+    // We didn't find the client's record (session has expired), or they
+    // are an anonymous user.
+    else {
+      $session = isset($this->user->session) ? $this->user->session : '';
+      $this->user = drupal_anonymous_user($session);
+    }
+
+    // Store the current user's uid in a protected property for verification
+    // against later.
+    $this->uid = $this->user->uid;
+
+    return $this->user->session;
+  }
+
+  /**
+   * 'Write' session handler assigned by session_set_save_handler().
+   *
+   * This method will be called by PHP to store the current user's
+   * session, which Drupal saves to the database.
+   *
+   * @param $sid
+   *   Session ID.
+   * @param $value
+   *   Serialized array of the session data.
+   * @return
+   *   This function will always return TRUE.
+   */
+  protected function writeHandler($sid, $value) {
+    // Ensure we are safe to proceed with writing session data.
+    if (!$this->writeCheck($value)) {
+      return FALSE;
+    }
+
+    db_merge('sessions')
+      ->key(array('sid' => $sid))
+      ->fields(array(
+        'uid' => $this->user->uid,
+        'cache' => isset($this->user->cache) ? $this->user->cache : 0,
+        'hostname' => ip_address(),
+        'session' => $value,
+        'timestamp' => REQUEST_TIME,
+      ))
+      ->execute();
+
+    // Last access time is updated no more frequently than once every 180 seconds.
+    // This reduces contention in the users table.
+    if ($this->user->uid && REQUEST_TIME - $this->user->access > variable_get('session_write_interval', 180)) {
+      db_update('users')
+        ->fields(array(
+          'access' => REQUEST_TIME
+        ))
+        ->condition('uid', $this->user->uid)
+        ->execute();
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Perform validation and security checks to ensure it is safe and appropriate
+   * to write data to the session table.
+   *
+   * First, if there is a mismatch between the user uid that was originally
+   * loaded into session data and Session::user->uid, then something has gone
+   * wrong, so we bail out without updating session data.
+   *
+   * Then, if the client doesn't have a session, and one isn't being created
+   * ($value), do nothing. This keeps crawlers out of the session table, which
+   * reduces memory and server load, and results in more useful statistics. We
+   * can't eliminate anonymous session table rows without breaking the
+   * "Who's Online" block.
+   *
+   * @param $value
+   *   Serialized array containing session data.
+   * @return bool
+   *   Returns a boolean indicating whether the session write should continue.
+   */
+  protected function writeCheck($value) {
+    if ($this->preventWrite || $this->uid != $this->user->uid ||
+        ($this->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
+  /**
+   * 'Destroy' session handler assigned by session_set_save_handler().
+   *
+   * Cleanup a specific session.
+   *
+   * @param string $sid
+   *   Session ID.
+   */
+  protected function destroyHandler($sid) {
+    db_delete('sessions')
+      ->condition('sid', $sid)
+      ->execute();
+  }
+
+  /**
+   * Session handler assigned by session_set_save_handler().
+   *
+   * Cleanup stalled sessions.
+   *
+   * @param int $lifetime
+   *   The value of session.gc_maxlifetime, passed by PHP.
+   *   Sessions not updated for more than $lifetime seconds will be removed.
+   */
+  private function gcHandler($lifetime) {
+    // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough
+    // value. For example, if you want user sessions to stay in your database
+    // for three weeks before deleting them, you need to set gc_maxlifetime
+    // to '1814400'. At that value, only after a user doesn't log in after
+    // three weeks (1814400 seconds) will his/her session be removed.
+    db_delete('sessions')
+      ->condition('timestamp', REQUEST_TIME - $lifetime, '<')
+      ->execute();
+    return TRUE;
+  }
+
+  /**
+   * Destructor magic method, called internally during PHP's shutdown sequence.
+   *
+   * By calling session_write_close() here via Session::sessionClose(), we are
+   * able to circumvent PHP's native shutdown ordering, which destructs objects
+   * before calling session Write and Close handlers.
+   *
+   * @return void
+   */
+  public function __destruct() {
+    $this->close();
+  }
 }
 
 /**
@@ -198,72 +415,3 @@ function drupal_session_count($timestamp = 0, $anonymous = TRUE) {
   $query->condition('uid', 0, $anonymous ? '=' : '>');
   return $query->execute()->fetchField();
 }
-
-/**
- * Session handler assigned by session_set_save_handler().
- *
- * Cleanup a specific session.
- *
- * @param string $sid
- *   Session ID.
- */
-function _sess_destroy_sid($sid) {
-  db_delete('sessions')
-    ->condition('sid', $sid)
-    ->execute();
-}
-
-/**
- * End a specific user's session(s).
- *
- * @param string $uid
- *   User ID.
- */
-function drupal_session_destroy_uid($uid) {
-  db_delete('sessions')
-    ->condition('uid', $uid)
-    ->execute();
-}
-
-/**
- * Session handler assigned by session_set_save_handler().
- *
- * Cleanup stalled sessions.
- *
- * @param int $lifetime
- *   The value of session.gc_maxlifetime, passed by PHP.
- *   Sessions not updated for more than $lifetime seconds will be removed.
- */
-function _sess_gc($lifetime) {
-  // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough
-  // value. For example, if you want user sessions to stay in your database
-  // for three weeks before deleting them, you need to set gc_maxlifetime
-  // to '1814400'. At that value, only after a user doesn't log in after
-  // three weeks (1814400 seconds) will his/her session be removed.
-  db_delete('sessions')
-    ->condition('timestamp', REQUEST_TIME - $lifetime, '<')
-    ->execute();
-  return TRUE;
-}
-
-/**
- * Determine whether to save session data of the current request.
- *
- * This function allows the caller to temporarily disable writing of
- * session data, should the request end while performing potentially
- * dangerous operations, such as manipulating the global $user object.
- * See http://drupal.org/node/218104 for usage.
- *
- * @param $status
- *   Disables writing of session data when FALSE, (re-)enables
- *   writing when TRUE.
- * @return
- *   FALSE if writing session data has been disabled. Otherwise, TRUE.
- */
-function drupal_save_session($status = NULL) {
-  static $save_session = TRUE;
-  if (isset($status)) {
-    $save_session = $status;
-  }
-  return $save_session;
-}
diff --git includes/theme.inc includes/theme.inc
index e0b3cbe..c985272 100644
--- includes/theme.inc
+++ includes/theme.inc
@@ -41,7 +41,8 @@ define('MARK_UPDATED', 2);
  * Initialize the theme system by loading the theme.
  */
 function init_theme() {
-  global $theme, $user, $custom_theme, $theme_key;
+  global $theme, $custom_theme, $theme_key;
+  $user = $_SESSION->user;
 
   // If $theme is already set, assume the others are set, too, and do nothing
   if (isset($theme)) {
@@ -1484,7 +1485,7 @@ function theme_box($title, $content, $region = 'main') {
  *   A string containing the marker.
  */
 function theme_mark($type = MARK_NEW) {
-  global $user;
+  $user = $_SESSION->user;
   if ($user->uid) {
     if ($type == MARK_NEW) {
       return ' <span class="marker">' . t('new') . '</span>';
@@ -1763,7 +1764,7 @@ function _theme_table_cell($cell, $header = FALSE) {
  * theme functions).
  */
 function template_preprocess(&$variables, $hook) {
-  global $user;
+  $user = $_SESSION->user;
   static $count = array();
 
   // Track run count for each hook to provide zebra striping.
diff --git install.php install.php
index 8679616..48049e6 100644
--- install.php
+++ install.php
@@ -1147,7 +1147,7 @@ function install_configure_form_validate($form, &$form_state) {
  * Form API submit for the site configuration form.
  */
 function install_configure_form_submit($form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
 
   variable_set('site_name', $form_state['values']['site_name']);
   variable_set('site_mail', $form_state['values']['site_mail']);
diff --git modules/block/block.module modules/block/block.module
index dcdc0dd..7d2fdd6 100644
--- modules/block/block.module
+++ modules/block/block.module
@@ -409,7 +409,8 @@ function block_list($region) {
  * Load blocks information from the database.
  */
 function _block_load_blocks() {
-  global $user, $theme_key;
+  global $theme_key;
+  $user = $_SESSION->user;
 
   $blocks = array();
   $rids = array_keys($user->roles);
@@ -527,7 +528,8 @@ function _block_render_blocks($region_blocks) {
  *   The string used as cache_id for the block.
  */
 function _block_get_cache_id($block) {
-  global $theme, $base_root, $user;
+  global $theme, $base_root;
+  $user = $_SESSION->user;
 
   // User 1 being out of the regular 'roles define permissions' schema,
   // it brings too many chances of having unwanted output get in the cache
diff --git modules/blog/blog.module modules/blog/blog.module
index 6b1c79f..b4dd403 100644
--- modules/blog/blog.module
+++ modules/blog/blog.module
@@ -170,7 +170,7 @@ function _blog_post_exists($account) {
  * Displays the most recent 10 blog titles.
  */
 function blog_block($op = 'list', $delta = '') {
-  global $user;
+  $user = $_SESSION->user;
   if ($op == 'list') {
     $block['recent']['info'] = t('Recent blog posts');
     return $block;
diff --git modules/blog/blog.pages.inc modules/blog/blog.pages.inc
index 57b5746..9178a50 100644
--- modules/blog/blog.pages.inc
+++ modules/blog/blog.pages.inc
@@ -10,7 +10,7 @@
  * Menu callback; displays a Drupal page containing recent blog entries of a given user.
  */
 function blog_page_user($account) {
-  global $user;
+  $user = $_SESSION->user;
 
   drupal_set_title($title = t("@name's blog", array('@name' => $account->name)), PASS_THROUGH);
 
@@ -53,7 +53,7 @@ function blog_page_user($account) {
  * Menu callback; displays a Drupal page containing recent blog entries of all users.
  */
 function blog_page_last() {
-  global $user;
+  $user = $_SESSION->user;
 
   $output = '';
   $items = array();
diff --git modules/blogapi/blogapi.module modules/blogapi/blogapi.module
index 1b51981..4e464d9 100644
--- modules/blogapi/blogapi.module
+++ modules/blogapi/blogapi.module
@@ -683,7 +683,7 @@ function blogapi_error($message) {
  * Ensure that the given user has permission to edit a blog.
  */
 function blogapi_validate_user($username, $password) {
-  global $user;
+  $user = $_SESSION->user;
 
   $user = user_authenticate(array('name' => $username, 'pass' => $password));
 
diff --git modules/comment/comment.module modules/comment/comment.module
index 53092ed..94fba7d 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -704,7 +704,7 @@ function comment_user_delete(&$edit, &$user, $category = NULL) {
  *   TRUE if the current user has acces to the comment, FALSE otherwise.
  */
 function comment_access($op, $comment) {
-  global $user;
+  $user = $_SESSION->user;
 
   if ($op == 'edit') {
     return ($user->uid && $user->uid == $comment->uid && comment_num_replies($comment->cid) == 0) || user_access('administer comments');
@@ -732,7 +732,7 @@ function comment_node_url() {
  *   is not saved, FALSE is returned.
  */
 function comment_save($edit) {
-  global $user;
+  $user = $_SESSION->user;
   if (user_access('post comments') && (user_access('administer comments') || node_comment_mode($edit['nid']) == COMMENT_NODE_READ_WRITE)) {
     if (!form_get_errors()) {
       $edit += array(
@@ -875,7 +875,7 @@ function comment_save($edit) {
  *   An associative array containing the links.
  */
 function comment_links($comment, $return = 1) {
-  global $user;
+  $user = $_SESSION->user;
   $links = array();
 
   // If viewing just this comment, link back to the node.
@@ -992,7 +992,7 @@ function comment_links($comment, $return = 1) {
  * to consider the trailing "/" so we use a substring only.
  */
 function comment_render($node, $cid = 0) {
-  global $user;
+  $user = $_SESSION->user;
   $output = '';
 
   if (user_access('access comments')) {
@@ -1192,7 +1192,7 @@ function comment_num_replies($pid) {
  * @return The result or FALSE on error.
  */
 function comment_num_new($nid, $timestamp = 0) {
-  global $user;
+  $user = $_SESSION->user;
 
   if ($user->uid) {
     // Retrieve the timestamp at which the current user last viewed this node.
@@ -1223,7 +1223,7 @@ function comment_num_new($nid, $timestamp = 0) {
  *   The original $edit.
  */
 function comment_validate($edit) {
-  global $user;
+  $user = $_SESSION->user;
 
   // Invoke other validation handlers.
   comment_invoke_comment($edit, 'validate');
@@ -1286,7 +1286,7 @@ function comment_validate($edit) {
  * @see comment_form_submit()
  */
 function comment_form(&$form_state, $edit, $title = NULL) {
-  global $user;
+  $user = $_SESSION->user;
   $op = isset($_POST['op']) ? $_POST['op'] : '';
   $node = node_load($edit['nid']);
 
@@ -1539,7 +1539,7 @@ function comment_form_box($edit, $title = NULL) {
  * @ingroup forms
  */
 function comment_form_add_preview($form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
   $edit = $form_state['values'];
   drupal_set_title(t('Preview comment'), PASS_THROUGH);
   $output = '';
@@ -1611,7 +1611,7 @@ function comment_form_add_preview($form, &$form_state) {
  * Validate comment form submissions.
  */
 function comment_form_validate($form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
   if ($user->uid === 0) {
     foreach (array('name', 'homepage', 'mail') as $field) {
       // Set cookie for 365 days.
@@ -1814,7 +1814,7 @@ function theme_comment_thread_expanded($comment, $node) {
  * @ingroup themeable
  */
 function theme_comment_post_forbidden($node) {
-  global $user;
+  $user = $_SESSION->user;
   static $authenticated_post_comments;
 
   if (!$user->uid) {
diff --git modules/comment/comment.pages.inc modules/comment/comment.pages.inc
index 64ffd50..be6ac54 100644
--- modules/comment/comment.pages.inc
+++ modules/comment/comment.pages.inc
@@ -14,7 +14,7 @@
  * @ingroup forms
  */
 function comment_edit($cid) {
-  global $user;
+  $user = $_SESSION->user;
   $comment = db_query('SELECT c.*, u.uid, u.name AS registered_name, u.data FROM {comment} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = :cid', array(':cid'=>$cid) )->fetchObject();
   $comment = drupal_unpack($comment);
   $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
diff --git modules/contact/contact.module modules/contact/contact.module
index 34cc1f3..4c3b037 100644
--- modules/contact/contact.module
+++ modules/contact/contact.module
@@ -115,7 +115,7 @@ function contact_menu() {
  * Determine if a user can access to the contact tab.
  */
 function _contact_user_tab_access($account) {
-  global $user;
+  $user = $_SESSION->user;
   if (!isset($account->contact)) {
     $account->contact = FALSE;
   }
diff --git modules/contact/contact.pages.inc modules/contact/contact.pages.inc
index 0e7d799..a6eac81 100644
--- modules/contact/contact.pages.inc
+++ modules/contact/contact.pages.inc
@@ -11,7 +11,7 @@
  * Site-wide contact page.
  */
 function contact_site_page() {
-  global $user;
+  $user = $_SESSION->user;
 
   if (!flood_is_allowed('contact', variable_get('contact_hourly_threshold', 3)) && !user_access('administer site-wide contact form')) {
     $output = t("You cannot send more than %number messages per hour. Please try again later.", array('%number' => variable_get('contact_hourly_threshold', 3)));
@@ -24,7 +24,7 @@ function contact_site_page() {
 }
 
 function contact_mail_page() {
-  global $user;
+  $user = $_SESSION->user;
 
   $form = $categories = array();
 
@@ -155,7 +155,7 @@ function contact_mail_page_submit($form, &$form_state) {
  * Personal contact page.
  */
 function contact_user_page($account) {
-  global $user;
+  $user = $_SESSION->user;
 
   if (!valid_email_address($user->mail)) {
     $output = t('You need to provide a valid e-mail address to contact other users. Please update your <a href="@url">user information</a> and try again.', array('@url' => url("user/$user->uid/edit", array('query' => 'destination=' . drupal_get_destination()))));
@@ -172,7 +172,7 @@ function contact_user_page($account) {
 }
 
 function contact_mail_user(&$form_state, $recipient) {
-  global $user;
+  $user = $_SESSION->user;
   $form['#token'] = $user->name . $user->mail;
   $form['recipient'] = array('#type' => 'value', '#value' => $recipient);
   $form['from'] = array('#type' => 'item',
@@ -206,7 +206,8 @@ function contact_mail_user(&$form_state, $recipient) {
  * Process the personal contact page form submission.
  */
 function contact_mail_user_submit($form, &$form_state) {
-  global $user, $language;
+  global $language;
+  $user = $_SESSION->user;
 
   $account = $form_state['values']['recipient'];
 
diff --git modules/dblog/dblog.admin.inc modules/dblog/dblog.admin.inc
index 20204ba..e01b8d1 100644
--- modules/dblog/dblog.admin.inc
+++ modules/dblog/dblog.admin.inc
@@ -261,8 +261,11 @@ function _dblog_format_message($dblog) {
  * @see dblog_filter_form_validate()
  */
 function dblog_filter_form() {
+  if (empty($_SESSION['dblog_overview_filter'])) {
+    $_SESSION['dblog_overview_filter'] = array();
+  }
   $session = &$_SESSION['dblog_overview_filter'];
-  $session = is_array($session) ? $session : array();
+
   $filters = dblog_filters();
 
   $form['filters'] = array(
diff --git modules/filter/filter.module modules/filter/filter.module
index 9af5fef..eaa450a 100644
--- modules/filter/filter.module
+++ modules/filter/filter.module
@@ -286,7 +286,7 @@ function filter_filter_tips($delta, $format, $long = FALSE) {
  * Retrieve a list of input formats.
  */
 function filter_formats($index = NULL) {
-  global $user;
+  $user = $_SESSION->user;
   static $formats;
 
   // Administrators can always use all input formats.
diff --git modules/forum/forum.module modules/forum/forum.module
index 11a7eac..0007d45 100644
--- modules/forum/forum.module
+++ modules/forum/forum.module
@@ -620,7 +620,8 @@ function _forum_topics_unread($term, $uid) {
 }
 
 function forum_get_topics($tid, $sortby, $forum_per_page) {
-  global $user, $forum_topic_list_header;
+  global $forum_topic_list_header;
+  $user = $_SESSION->user; 
 
   $forum_topic_list_header = array(
     NULL,
@@ -680,7 +681,7 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
  * Finds the first unread node for a given forum.
  */
 function _forum_new($tid) {
-  global $user;
+  $user = $_SESSION->user;
 
   $sql = "SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND h.nid IS NULL AND n.created > %d ORDER BY created";
   $sql = db_rewrite_sql($sql);
@@ -703,7 +704,7 @@ function _forum_new($tid) {
  * @see forums.tpl.php
  */
 function template_preprocess_forums(&$variables) {
-  global $user;
+  $user = $_SESSION->user;
 
   $vid = variable_get('forum_nav_vocabulary', '');
   $vocabulary = taxonomy_vocabulary_load($vid);
@@ -806,7 +807,7 @@ function template_preprocess_forums(&$variables) {
  * @see theme_forum_list()
  */
 function template_preprocess_forum_list(&$variables) {
-  global $user;
+  $user = $_SESSION->user;
   $row = 0;
   // Sanitize each forum so that the template can safely print the data.
   foreach ($variables['forums'] as $id => $forum) {
@@ -982,7 +983,7 @@ function template_preprocess_forum_submitted(&$variables) {
 }
 
 function _forum_user_last_visit($nid) {
-  global $user;
+  $user = $_SESSION->user;
   static $history = array();
 
   if (empty($history)) {
diff --git modules/node/node.module modules/node/node.module
index 3cbd4b5..9d52cb0 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -187,7 +187,7 @@ function theme_node_list($items, $title = NULL) {
  * Update the 'last viewed' timestamp of the specified node for current user.
  */
 function node_tag_new($nid) {
-  global $user;
+  $user = $_SESSION->user;
 
   if ($user->uid) {
     if (node_last_viewed($nid)) {
@@ -204,7 +204,7 @@ function node_tag_new($nid) {
  * specified node.
  */
 function node_last_viewed($nid) {
-  global $user;
+  $user = $_SESSION->user;
   static $history;
 
   if (!isset($history[$nid])) {
@@ -225,7 +225,7 @@ function node_last_viewed($nid) {
  *   One of the MARK constants.
  */
 function node_mark($nid, $timestamp) {
-  global $user;
+  $user = $_SESSION->user;
   static $cache;
 
   if (!$user->uid) {
@@ -945,7 +945,7 @@ function node_validate($node, $form = array()) {
  * Prepare node for save and allow modules to make changes.
  */
 function node_submit($node) {
-  global $user;
+  $user = $_SESSION->user;
 
   // Convert the node to an object, if necessary.
   $node = (object)$node;
@@ -989,7 +989,7 @@ function node_submit($node) {
 function node_save(&$node) {
   // Let modules modify the node before it is saved to the database.
   node_invoke_nodeapi($node, 'presave');
-  global $user;
+  $user = $_SESSION->user;
 
   $node->is_new = FALSE;
 
@@ -2165,7 +2165,7 @@ function node_search_validate($form, &$form_state) {
  *   TRUE if the operation may be performed.
  */
 function node_access($op, $node, $account = NULL) {
-  global $user;
+  $user = $_SESSION->user;
 
   if (!$node) {
     return FALSE;
@@ -2301,7 +2301,7 @@ function _node_access_where_sql($op = 'view', $node_access_alias = 'na', $accoun
 function node_access_grants($op, $account = NULL) {
 
   if (!isset($account)) {
-    $account = $GLOBALS['user'];
+    $account = &$_SESSION->user;
   }
 
   return array_merge(array('all' => array(0)), module_invoke_all('node_grants', $account, $op));
diff --git modules/node/node.pages.inc modules/node/node.pages.inc
index 3e7411a..7214d05 100644
--- modules/node/node.pages.inc
+++ modules/node/node.pages.inc
@@ -50,7 +50,7 @@ function theme_node_add_list($content) {
  * Present a node submission form or a set of links to such forms.
  */
 function node_add($type) {
-  global $user;
+  $user = $_SESSION->user;
 
   $types = node_get_types();
   $type = isset($type) ? str_replace('-', '_', $type) : NULL;
@@ -78,7 +78,7 @@ function node_object_prepare(&$node) {
     foreach (array('status', 'promote', 'sticky') as $key) {
       $node->$key = in_array($key, $node_options);
     }
-    global $user;
+    $user = $_SESSION->user;
     $node->uid = $user->uid;
     $node->created = REQUEST_TIME;
   }
@@ -96,7 +96,7 @@ function node_object_prepare(&$node) {
  * Generate the node add/edit form array.
  */
 function node_form(&$form_state, $node) {
-  global $user;
+  $user = $_SESSION->user;
 
   if (isset($form_state['node'])) {
     $node = $form_state['node'] + (array)$node;
@@ -428,7 +428,7 @@ function theme_node_preview($node) {
 }
 
 function node_form_submit($form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
 
   $node = node_form_submit_build_node($form, $form_state);
   $insert = empty($node->nid);
diff --git modules/php/php.module modules/php/php.module
index 7090239..52793d3 100644
--- modules/php/php.module
+++ modules/php/php.module
@@ -50,7 +50,7 @@ print t(\'Welcome visitor! Thank you for visiting.\');
 </pre>') . '</li>';
         $output .= '<li>' . t('<p>To display the name of a registered user, use this instead:</p>
 <pre>
-global $user;
+$user = $_SESSION->user;
 if ($user->uid) {
   print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => $user->name));
 }
diff --git modules/poll/poll.module modules/poll/poll.module
index 9c79fcc..bb5e626 100644
--- modules/poll/poll.module
+++ modules/poll/poll.module
@@ -193,7 +193,7 @@ function poll_node_info() {
  * Implementation of hook_form().
  */
 function poll_form(&$node, $form_state) {
-  global $user;
+  $user = $_SESSION->user;
 
   $admin = user_access('administer nodes') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid);
 
@@ -450,7 +450,7 @@ function poll_validate($node) {
  * Implementation of hook_load().
  */
 function poll_load($nodes) {
-  global $user;
+  $user = $_SESSION->user;
   foreach ($nodes as $node) {
     $poll = db_query("SELECT runtime, active FROM {poll} WHERE nid = :nid", array(':nid' => $node->nid))->fetch();
 
@@ -542,7 +542,7 @@ function poll_delete($node) {
  *   rendering of the poll.
  */
 function poll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
-  global $user;
+  $user = $_SESSION->user;
   $output = '';
 
   // Special display for side-block
@@ -644,7 +644,7 @@ function poll_vote($form, &$form_state) {
   $node = $form['#node'];
   $choice = $form_state['values']['choice'];
 
-  global $user;
+  $user = $_SESSION->user;
   if ($user->uid) {
     db_query('INSERT INTO {poll_vote} (nid, chid, uid) VALUES (%d, %d, %d)', $node->nid, $choice, $user->uid);
   }
@@ -818,7 +818,7 @@ function poll_cancel_form(&$form_state, $nid) {
  */
 function poll_cancel($form, &$form_state) {
   $node = node_load($form['#nid']);
-  global $user;
+  $user = $_SESSION->user;
 
   if ($user->uid) {
     db_query('DELETE FROM {poll_vote} WHERE nid = %d and uid = %d', $node->nid, $user->uid);
diff --git modules/profile/profile.module modules/profile/profile.module
index 792fc5f..bbb769e 100644
--- modules/profile/profile.module
+++ modules/profile/profile.module
@@ -330,7 +330,7 @@ function profile_view_profile(&$user) {
   profile_load_profile($user);
 
   // Show private fields to administrators and people viewing their own account.
-  if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
+  if (user_access('administer users') || $_SESSION->user->uid == $user->uid) {
     $result = db_query('SELECT * FROM {profile_field} WHERE visibility != %d ORDER BY category, weight', PROFILE_HIDDEN);
   }
   else {
diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index f5af622..bbe75a3 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -495,7 +495,7 @@ class DrupalWebTestCase {
       $defaults['date'] = format_date($defaults['created'], 'custom', 'Y-m-d H:i:s O');
     }
     if (empty($settings['uid'])) {
-      global $user;
+      $user = $_SESSION->user;
       $defaults['uid'] = $user->uid;
     }
     $node = ($settings + $defaults);
diff --git modules/simpletest/tests/file.test modules/simpletest/tests/file.test
index 6990dd8..701f6d7 100644
--- modules/simpletest/tests/file.test
+++ modules/simpletest/tests/file.test
@@ -254,12 +254,11 @@ class FileValidatorTest extends DrupalWebTestCase {
    * Test file_validate_size().
    */
   function testFileValidateSize() {
-    global $user;
+    $user = $_SESSION->user;
     $original_user = $user;
-    drupal_save_session(FALSE);
 
     // Run these test as uid = 1.
-    $user = user_load(array('uid' => 1));
+    $_SESSION->user = user_load(array('uid' => 1));
 
     $file = new stdClass();
     $file->filesize = 999999;
@@ -267,7 +266,7 @@ class FileValidatorTest extends DrupalWebTestCase {
     $this->assertEqual(count($errors), 0, t('No size limits enforced on uid=1.'), 'File');
 
     // Run these tests as a regular user.
-    $user = $this->drupalCreateUser();
+    $_SESSION->user = $this->drupalCreateUser();
 
     // Create a file with a size of 1000 bytes, and quotas of only 1 byte.
     $file = new stdClass();
@@ -281,8 +280,7 @@ class FileValidatorTest extends DrupalWebTestCase {
     $errors = file_validate_size($file, 1, 1);
     $this->assertEqual(count($errors), 2, t('Errors for both the file and their limit.'), 'File');
 
-    $user = $original_user;
-    drupal_save_session(TRUE);
+    $_SESSION->user = $original_user;
   }
 }
 
diff --git modules/simpletest/tests/session.test modules/simpletest/tests/session.test
index b1946a3..8906bc1 100644
--- modules/simpletest/tests/session.test
+++ modules/simpletest/tests/session.test
@@ -20,15 +20,9 @@ class SessionTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Tests for drupal_save_session() and drupal_session_regenerate().
+   * Tests for Session::regenerate().
    */
   function testSessionSaveRegenerate() {
-    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when initially called with no arguments.'), t('Session'));
-    $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
-    $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
-    $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
-    $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
-
     // Test session hardening code from SA-2008-044.
     $user = $this->drupalCreateUser(array('access content'));
     // Enable sessions.
@@ -88,7 +82,6 @@ class SessionTestCase extends DrupalWebTestCase {
     $this->drupalGet('session-test/no-set/' . $value_2);
     $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
     $this->drupalGet('session-test/get');
-    $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
 
     // Switch browser cookie to anonymous user, then back to user 1.
     $this->sessionReset();
@@ -113,7 +106,6 @@ class SessionTestCase extends DrupalWebTestCase {
     $this->drupalGet('session-test/no-set/' . $value_4);
     $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
     $this->drupalGet('session-test/get');
-    $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
 
     // Logout and get first user back in. Sessions shouldn't persist through
     // logout, so the data won't be on the page.
diff --git modules/simpletest/tests/session_test.module modules/simpletest/tests/session_test.module
index e7efbf2..7afed6c 100644
--- modules/simpletest/tests/session_test.module
+++ modules/simpletest/tests/session_test.module
@@ -60,7 +60,7 @@ function _session_test_set($value) {
  * anyway.
  */
 function _session_test_no_set($value) {
-  drupal_save_session(FALSE);
+  $_SESSION->preventWrite = TRUE;
   _session_test_set($value);
   return t('session saving was disabled, and then %val was set', array('%val' => $value));
 }
diff --git modules/statistics/statistics.module modules/statistics/statistics.module
index 4687046..f4a9ee5 100644
--- modules/statistics/statistics.module
+++ modules/statistics/statistics.module
@@ -43,7 +43,7 @@ function statistics_help($path, $arg) {
  * This is where statistics are gathered on page accesses.
  */
 function statistics_exit() {
-  global $user;
+  $user = $_SESSION->user;
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
 
diff --git modules/system/system.api.php modules/system/system.api.php
index 55c21bd..dc6b6ec 100644
--- modules/system/system.api.php
+++ modules/system/system.api.php
@@ -970,7 +970,7 @@ function hook_modules_uninstalled($modules) {
  *   The unaliased Drupal path that is being linked to.
  */
 function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
-  global $user;
+  $user = $_SESSION->user;
 
   // Change all 'node' to 'article'.
   if (preg_match('|^node(/.*)|', $path, $matches)) {
diff --git modules/system/system.module modules/system/system.module
index 155c5fb..1671f57 100644
--- modules/system/system.module
+++ modules/system/system.module
@@ -814,7 +814,7 @@ function system_user_login(&$edit, &$user, $category = NULL) {
  * Add the time zone field to the user edit and register forms.
  */
 function system_user_timezone(&$edit, &$form) {
-  global $user;
+  $user = $_SESSION->user;
   $form['timezone'] = array(
     '#type' => 'fieldset',
     '#title' => t('Locale settings'),
@@ -1436,7 +1436,7 @@ function confirm_form($form, $question, $path, $description = NULL, $yes = NULL,
  * Determine if a user is in compact mode.
  */
 function system_admin_compact_mode() {
-  global $user;
+  $user = $_SESSION->user;
   return (isset($user->admin_compact_mode)) ? $user->admin_compact_mode : variable_get('admin_compact_mode', FALSE);
 }
 
@@ -1447,7 +1447,7 @@ function system_admin_compact_mode() {
  *   Valid values are 'on' and 'off'.
  */
 function system_admin_compact_page($mode = 'off') {
-  global $user;
+  $user = $_SESSION->user;
   user_save($user, array('admin_compact_mode' => ($mode == 'on')));
   drupal_goto(drupal_get_destination());
 }
@@ -1930,7 +1930,7 @@ function system_send_email_action_submit($form, $form_state) {
  * Implementation of a configurable Drupal action. Sends an email.
  */
 function system_send_email_action($object, $context) {
-  global $user;
+  $user = $_SESSION->user;
 
   switch ($context['hook']) {
     case 'nodeapi':
@@ -2053,7 +2053,7 @@ function system_message_action_submit($form, $form_state) {
  * A configurable Drupal action. Sends a message to the current user's screen.
  */
 function system_message_action(&$object, $context = array()) {
-  global $user;
+  $user = $_SESSION->user;
   $variables = array(
     '%site_name' => variable_get('site_name', 'Drupal'),
     '%username' => $user->name ? $user->name : variable_get('anonymous', t('Anonymous')),
diff --git modules/tracker/tracker.module modules/tracker/tracker.module
index fa2ee62..1bf0dd9 100644
--- modules/tracker/tracker.module
+++ modules/tracker/tracker.module
@@ -61,7 +61,7 @@ function tracker_menu() {
  */
 function _tracker_myrecent_access($account) {
   // This path is only allowed for authenticated users looking at their own posts.
-  return $account->uid && ($GLOBALS['user']->uid == $account->uid) && user_access('access content');
+  return $account->uid && ($_SESSION->user->uid == $account->uid) && user_access('access content');
 }
 
 /**
diff --git modules/trigger/trigger.test modules/trigger/trigger.test
index c539ad5..a3469fc 100644
--- modules/trigger/trigger.test
+++ modules/trigger/trigger.test
@@ -21,7 +21,7 @@ class TriggerContentTestCase extends DrupalWebTestCase {
    * Various tests, all in one function to assure they happen in the right order.
    */
   function testActionsContent() {
-    global $user;
+    $user = $_SESSION->user;
     $content_actions = array('node_publish_action', 'node_unpublish_action', 'node_make_sticky_action', 'node_make_unsticky_action', 'node_promote_action', 'node_unpromote_action');
 
     foreach ($content_actions as $action) {
diff --git modules/upload/upload.module modules/upload/upload.module
index 30beec7..ed4affc 100644
--- modules/upload/upload.module
+++ modules/upload/upload.module
@@ -172,7 +172,7 @@ function upload_file_download($filepath) {
  *   A node object to associate with uploaded files.
  */
 function upload_node_form_submit(&$form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
 
   $limits = _upload_file_limits($user);
   $validators = array(
@@ -486,7 +486,7 @@ function upload_save(&$node) {
 }
 
 function _upload_form($node) {
-  global $user;
+  $user = $_SESSION->user;
 
   $form = array(
     '#theme' => 'upload_form_new',
diff --git modules/user/user.admin.inc modules/user/user.admin.inc
index 5fee6f9..1b69112 100644
--- modules/user/user.admin.inc
+++ modules/user/user.admin.inc
@@ -33,8 +33,10 @@ function user_admin($callback_arg = '') {
  * @see user_filter_form_submit()
  */
 function user_filter_form() {
+  if (empty($_SESSION['user_overview_filter'])) {
+    $_SESSION['user_overview_filter'] = array();
+  }
   $session = &$_SESSION['user_overview_filter'];
-  $session = is_array($session) ? $session : array();
   $filters = user_filters();
 
   $i = 0;
diff --git modules/user/user.module modules/user/user.module
index 637a204..6260689 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -126,9 +126,7 @@ function user_external_login($account, $edit = array()) {
   }
 
   // Valid login.
-  global $user;
-  $user = $account;
-  user_authenticate_finalize($state['values']);
+  $_SESSION->userLogin($account, $state['values']);
   return TRUE;
 }
 
@@ -275,14 +273,16 @@ function user_save($account, $edit = array(), $category = 'account') {
 
     // Delete a blocked user's sessions to kick them if they are online.
     if (isset($edit['status']) && $edit['status'] == 0) {
-      drupal_session_destroy_uid($account->uid);
+      DrupalSession::destroyByUid($account->uid);
     }
 
     // If the password changed, delete all open sessions and recreate
     // the current one.
     if (!empty($edit['pass'])) {
-      drupal_session_destroy_uid($account->uid);
-      drupal_session_regenerate();
+      DrupalSession::destroyByUid($account->uid);
+      if ($account->uid == $_SESSION->user->uid) {
+        $_SESSION->regenerate();
+      }
     }
 
     // Refresh user object.
@@ -522,7 +522,7 @@ function user_role_permissions($roles = array(), $reset = FALSE) {
  * can perform all actions.
  */
 function user_access($string, $account = NULL, $reset = FALSE) {
-  global $user;
+  $user = $_SESSION->user;
   static $perm = array();
 
   if ($reset) {
@@ -736,7 +736,7 @@ function user_login_block() {
  * Implementation of hook_block().
  */
 function user_block($op = 'list', $delta = '', $edit = array()) {
-  global $user;
+  $user = $_SESSION->user;
 
   if ($op == 'list') {
     $blocks['login']['info'] = t('User login');
@@ -893,11 +893,11 @@ function theme_user_list($users, $title = NULL) {
 
 function user_is_anonymous() {
   // Menu administrators can see items for anonymous when administering.
-  return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
+  return !$_SESSION->user->uid || !empty($GLOBALS['menu_admin']);
 }
 
 function user_is_logged_in() {
-  return (bool)$GLOBALS['user']->uid;
+  return (bool) $_SESSION->user->uid;
 }
 
 function user_register_access() {
@@ -908,7 +908,7 @@ function user_view_access($account) {
   return $account && $account->uid &&
     (
       // Always let users view their own profile.
-      ($GLOBALS['user']->uid == $account->uid) ||
+      ($_SESSION->user->uid == $account->uid) ||
       // Administrators can view all accounts.
       user_access('administer users') ||
       // The user is not blocked and logged in at least once.
@@ -920,11 +920,11 @@ function user_view_access($account) {
  * Access callback for user account editing.
  */
 function user_edit_access($account) {
-  return (($GLOBALS['user']->uid == $account->uid) || user_access('administer users')) && $account->uid > 0;
+  return (($_SESSION->user->uid == $account->uid) || user_access('administer users')) && $account->uid > 0;
 }
 
 function user_load_self($arg) {
-  $arg[1] = user_load($GLOBALS['user']->uid);
+  $arg[1] = user_load($_SESSION->user->uid);
   return $arg;
 }
 
@@ -1110,7 +1110,7 @@ function user_init() {
 }
 
 function user_uid_optional_load($arg) {
-  return user_load(isset($arg) ? $arg : $GLOBALS['user']->uid);
+  return user_load(isset($arg) ? $arg : $_SESSION->user->uid);
 }
 
 /**
@@ -1159,14 +1159,14 @@ function user_uid_optional_to_arg($arg) {
   // Give back the current user uid when called from eg. tracker, aka.
   // with an empty arg. Also use the current user uid when called from
   // the menu with a % for the current account link.
-  return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg;
+  return empty($arg) || $arg == '%' ? $_SESSION->user->uid : $arg;
 }
 
 /**
  * Menu item title callback - use the user name if it's not the current user.
  */
 function user_page_title($account) {
-  if ($account->uid == $GLOBALS['user']->uid) {
+  if ($account->uid == $_SESSION->user->uid) {
     return t('My account');
   }
   return $account->name;
@@ -1229,7 +1229,7 @@ function user_set_authmaps($account, $authmaps) {
  * @ingroup forms
  */
 function user_login(&$form_state) {
-  global $user;
+  $user = $_SESSION->user;
 
   // If we are already logged on, go to the user page instead.
   if ($user->uid) {
@@ -1291,7 +1291,7 @@ function user_login_name_validate($form, &$form_state) {
 
 /**
  * A validate handler on the login form. Check supplied username/password
- * against local users table. If successful, sets the global $user object.
+ * against local users table. If successful, sets the $user = $_SESSION->user object.
  */
 function user_login_authenticate_validate($form, &$form_state) {
   user_authenticate($form_state['values']);
@@ -1302,7 +1302,7 @@ function user_login_authenticate_validate($form, &$form_state) {
  * error if user has not been authenticated yet.
  */
 function user_login_final_validate($form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
   if (!$user->uid) {
     form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
     watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
@@ -1320,8 +1320,6 @@ function user_login_final_validate($form, &$form_state) {
  *  A $user object, if successful.
  */
 function user_authenticate($form_values = array()) {
-  global $user;
-
   $password = trim($form_values['pass']);
   // Name and pass keys are required.
   if (!empty($form_values['name']) && !empty($password)) {
@@ -1337,45 +1335,20 @@ function user_authenticate($form_values = array()) {
            }
         }
         $account = user_load(array('uid' => $account->uid, 'status' => 1));
-        $user = $account;
-        user_authenticate_finalize($form_values);
-        return $user;
+        return $_SESSION->userLogin($account, $form_values);
       }
     }
   }
 }
 
 /**
- * Finalize the login process. Must be called when logging in a user.
- *
- * The function records a watchdog message about the new session, saves the
- * login timestamp, calls hook_user op 'login' and generates a new session.
- *
- * $param $edit
- *   This array is passed to hook_user op login.
- */
-function user_authenticate_finalize(&$edit) {
-  global $user;
-  watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
-  // Update the user table timestamp noting user has logged in.
-  // This is also used to invalidate one-time login links.
-  $user->login = REQUEST_TIME;
-  db_query("UPDATE {users} SET login = %d WHERE uid = %d", $user->login, $user->uid);
-  // Regenerate the session ID to prevent against session fixation attacks.
-  // This is called before hook_user in case one of those functions fails
-  // or incorrectly does a redirect which would leave the old session in place.
-  drupal_session_regenerate();
-  user_module_invoke('login', $edit, $user);
-}
-
-/**
  * Submit handler for the login form. Redirects the user to a page.
  *
  * The user is redirected to the My Account page. Setting the destination in
  * the query string (as done by the user login block) overrides the redirect.
  */
 function user_login_submit($form, &$form_state) {
-  global $user;
+  $user = $_SESSION->user;
   if ($user->uid) {
     $form_state['redirect'] = 'user/' . $user->uid;
     return;
@@ -1384,15 +1357,13 @@ function user_login_submit($form, &$form_state) {
 
 /**
  * Helper function for authentication modules. Either login in or registers
- * the current user, based on username. Either way, the global $user object is
+ * the current user, based on username. Either way, the $user = $_SESSION->user object is
  * populated based on $name.
  */
 function user_external_login_register($name, $module) {
-  global $user;
-
   $existing_user = user_load(array('name' => $name));
   if (isset($existing_user->uid)) {
-    $user = $existing_user;
+    $_SESSION->user = $existing_user;
   }
   else {
     // Register this new user.
@@ -1410,8 +1381,8 @@ function user_external_login_register($name, $module) {
       return;
     }
     user_set_authmaps($account, array("authname_$module" => $name));
-    $user = $account;
-    watchdog('user', 'New external user: %name using module %module.', array('%name' => $name, '%module' => $module), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $user->uid . '/edit'));
+    $_SESSION->user = $account;
+    watchdog('user', 'New external user: %name using module %module.', array('%name' => $name, '%module' => $module), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $_SESSION->user->uid . '/edit'));
   }
 }
 
@@ -1577,7 +1548,7 @@ function _user_edit_submit($uid, &$edit) {
  */
 function user_delete($edit, $uid) {
   $account = user_load(array('uid' => $uid));
-  drupal_session_destroy_uid($uid);
+  DrupalSession::destroyByUid($uid);
   _user_mail_notify('status_deleted', $account);
   module_invoke_all('user_delete', $edit, $account);
   db_query('DELETE FROM {users} WHERE uid = %d', $uid);
@@ -1873,7 +1844,7 @@ function user_multiple_delete_confirm_submit($form, &$form_state) {
  * Implementation of hook_help().
  */
 function user_help($path, $arg) {
-  global $user;
+  $user = $_SESSION->user;
 
   switch ($path) {
     case 'admin/help#user':
@@ -2147,7 +2118,7 @@ function _user_mail_notify($op, $account, $language = NULL) {
  */
 function _user_password_dynamic_validation() {
   static $complete = FALSE;
-  global $user;
+  $user = $_SESSION->user;
   // Only need to do once per page.
   if (!$complete) {
     drupal_add_js(drupal_get_path('module', 'user') . '/user.js');
@@ -2227,11 +2198,11 @@ function user_block_user_action(&$object, $context = array()) {
     $uid = $context['uid'];
   }
   else {
-    global $user;
+    $user = $_SESSION->user;
     $uid = $user->uid;
   }
   db_query("UPDATE {users} SET status = 0 WHERE uid = %d", $uid);
-  drupal_session_destroy_uid($uid);
+  DrupalSession::destroyByUid($uid);
   watchdog('action', 'Blocked user %name.', array('%name' => check_plain($user->name)));
 }
 
@@ -2348,7 +2319,7 @@ function user_register_submit($form, &$form_state) {
  * @see user_register_submit()
  */
 function user_register() {
-  global $user;
+  $user = $_SESSION->user;
 
   $admin = user_access('administer users');
 
diff --git modules/user/user.pages.inc modules/user/user.pages.inc
index fa5c992..8c5c0b4 100644
--- modules/user/user.pages.inc
+++ modules/user/user.pages.inc
@@ -74,10 +74,8 @@ function user_pass_submit($form, &$form_state) {
  * Menu callback; process one time login link and redirects to the user page on success.
  */
 function user_pass_reset(&$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
-  global $user;
-
   // Check if the user is already logged in. The back button is often the culprit here.
-  if ($user->uid) {
+  if ($_SESSION->user->uid) {
     drupal_set_message(t('You have already used this one-time login link. It is not necessary to use this link to login anymore. You are already logged in.'));
     drupal_goto();
   }
@@ -96,13 +94,12 @@ function user_pass_reset(&$form_state, $uid, $timestamp, $hashed_pass, $action =
         // First stage is a confirmation form, then login
         if ($action == 'login') {
           watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp));
-          // Set the new user.
-          $user = $account;
-          // user_authenticate_finalize() also updates the login timestamp of the
-          // user, which invalidates further use of the one-time login link.
-          user_authenticate_finalize($form_state['values']);
+          // Session singleton takes care of updating relevant timestamp and
+          // session data, and registers the provided account as the new user.
+          // One one-time login link will also be invalidated.
+          $_SESSION->userLogin($account, $form_state['values']);
           drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to login. Please change your password.'));
-          drupal_goto('user/' . $user->uid . '/edit');
+          drupal_goto('user/' . $_SESSION->user->uid . '/edit');
         }
         else {
           $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to login to the site and change your password.</p>', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout))));
@@ -129,17 +126,7 @@ function user_pass_reset(&$form_state, $uid, $timestamp, $hashed_pass, $action =
  * Menu callback; logs the current user out, and redirects to the home page.
  */
 function user_logout() {
-  global $user;
-
-  watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
-
-  // Destroy the current session:
-  session_destroy();
-  module_invoke_all('user_logout', NULL, $user);
-
-  // Load the anonymous user
-  $user = drupal_anonymous_user();
-
+  $_SESSION->userLogout();
   drupal_goto();
 }
 
@@ -354,9 +341,8 @@ function user_edit_submit($form, &$form_state) {
  * users.
  */
 function user_page() {
-  global $user;
-  if ($user->uid) {
-    menu_set_active_item('user/' . $user->uid);
+  if ($_SESSION->user->uid) {
+    menu_set_active_item('user/' . $_SESSION->user->uid);
     return menu_execute_active_handler();
   }
   else {
