diff --git a/mongodb_session/mongodb_session.inc b/mongodb_session/mongodb_session.inc new file mode 100644 index 0000000..527db11 --- /dev/null +++ b/mongodb_session/mongodb_session.inc @@ -0,0 +1,256 @@ +update(array('sid' => $old_session_id), array('$set' => array('sid' => session_id()))); +} + +/** + * Counts how many users have sessions. Can count either anonymous sessions or authenticated sessions. + * + * @param int $timestamp + * A Unix timestamp representing a point of time in the past. + * The default is 0, which counts all existing sessions. + * @param boolean $anonymous + * TRUE counts only anonymous users. + * FALSE counts only authenticated users. + * @return int + * The number of users with sessions. + */ +function sess_count($timestamp = 0, $anonymous = true) { + $conditions = array( + 'timestamp' => array('$gte' => $timestamp), + 'uid' => $anonymous ? 0 : array('$gt' => 0) + ); + return mongodb_collection(variable_get('mongodb_session', 'session'))->find($conditions)->count(); +} + +/** + * 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 session_save_session($status = NULL) { + static $save_session = TRUE; + if (isset($status)) { + $save_session = $status; + } + return ($save_session); +} + +/** + * 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 $sid + * 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($sid) { + 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 ''; + } + + // Otherwise, if the session is still active, we have a record of the client's session in the database. + $user = (object) mongodb_collection(variable_get('mongodb_session', 'session'))->findOne(array('sid' => $sid)); + + // We found the client's session record and they are an authenticated, + // active user. + if ($user && $user->uid > 0 && $user->status == 1) { + // This is done to unserialize the data member of $user + $user = drupal_unpack($user); + + // Add roles element to $user + $user->roles = array(); + $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; + $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; + } + } + // We didn't find the client's record (session has expired), or they are + // blocked, or they are an anonymous user. + else { + $session = isset($user->session) ? $user->session : ''; + $user = drupal_anonymous_user($session); + } + + return $user->session; +} + +/** + * 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 $sid + * Session ID. + * @param $value + * Serialized array of the session data. + * @return + * This function will always return TRUE. + */ +function sess_write($sid, $value) { + global $user; + + if (!session_save_session()) { + // We don't have anything to do if we are not allowed to save the session. + return; + } + + $fields = array( + 'uid' => $user->uid, + 'cache' => isset($user->cache) ? (int)$user->cache : 0, + 'hostname' => ip_address(), + 'session' => $value, + 'timestamp' => $_SERVER['REQUEST_TIME'], + ); + $key = array('sid' => $sid); + + mongodb_collection(variable_get('mongodb_session', 'session')) + ->update($key, $key + $fields + (array)$user, array('upsert' => TRUE)); + + // Last access time is updated no more frequently than once every 180 seconds. + // This reduces contention in the users table. + if ($user->uid && $_SERVER['REQUEST_TIME'] - $user->access > variable_get('session_write_interval', 180)) { + db_query("UPDATE {users} SET access = %d WHERE uid = %d", $_SERVER['REQUEST_TIME'], $user->uid); + } + + return TRUE; +} + +/** + * Session handler assigned by session_set_save_handler(). + * + * Cleanup a specific session. + * + * @param string $sid + * Session ID. + */ +function sess_destroy_sid($sid) { + mongodb_collection(variable_get('mongodb_session', 'session')) + ->remove(array('sid' => $sid)); +} + +/** + * End a specific user's session(s). + * + * @param string $uid + * User ID. + */ +function sess_destroy_uid($uid) { + mongodb_collection(variable_get('mongodb_session', 'session')) + ->remove(array('uid' => $uid)); +} + +/** + * 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. + mongodb_collection(variable_get('mongodb_session', 'session')) + ->remove(array('timestamp' => array('$lt' => $_SERVER['REQUEST_TIME'] - $lifetime))); + return TRUE; +} diff --git a/mongodb_session/mongodb_session.info b/mongodb_session/mongodb_session.info new file mode 100644 index 0000000..2bed6ca --- /dev/null +++ b/mongodb_session/mongodb_session.info @@ -0,0 +1,6 @@ + +name = MongoDB Session +description = Store session information in MongoDB +package = MongoDB +core = 6.x +dependencies[] = mongodb diff --git a/mongodb_session/mongodb_session.module b/mongodb_session/mongodb_session.module new file mode 100644 index 0000000..ab9fd82 --- /dev/null +++ b/mongodb_session/mongodb_session.module @@ -0,0 +1,16 @@ +