| 1 | | <?php |
| 2 | | // $Id: session.inc,v 1.62 2008/11/01 21:27:38 dries Exp $ |
| 3 | | |
| 4 | | /** |
| 5 | | * @file |
| 6 | | * User session handling functions. |
| 7 | | * |
| 8 | | * The user-level session storage handlers: |
| 9 | | * - _sess_open() |
| 10 | | * - _sess_close() |
| 11 | | * - _sess_read() |
| 12 | | * - _sess_write() |
| 13 | | * - _sess_destroy_sid() |
| 14 | | * - _sess_gc() |
| 15 | | * are assigned by session_set_save_handler() in bootstrap.inc and are
called |
| 16 | | * automatically by PHP. These functions should not be called directly.
Session |
| 17 | | * data should instead be accessed via the $_SESSION superglobal. |
| 18 | | */ |
| 19 | | |
| 20 | | /** |
| 21 | | * Session handler assigned by session_set_save_handler(). |
| 22 | | * |
| 23 | | * This function is used to handle any initialization, such as file paths
or |
| 24 | | * database connections, that is needed before accessing session data.
Drupal |
| 25 | | * does not need to initialize anything in this function. |
| 26 | | * |
| 27 | | * This function should not be called directly. |
| 28 | | * |
| 29 | | * @return |
| 30 | | * This function will always return TRUE. |
| 31 | | */ |
| 32 | 2367 | function _sess_open() { |
| 33 | 2367 | return TRUE; |
| 34 | 0 | } |
| 35 | | |
| 36 | | /** |
| 37 | | * Session handler assigned by session_set_save_handler(). |
| 38 | | * |
| 39 | | * This function is used to close the current session. Because Drupal
stores |
| 40 | | * session data in the database immediately on write, this function does |
| 41 | | * not need to do anything. |
| 42 | | * |
| 43 | | * This function should not be called directly. |
| 44 | | * |
| 45 | | * @return |
| 46 | | * This function will always return TRUE. |
| 47 | | */ |
| 48 | 2367 | function _sess_close() { |
| 49 | 577 | return TRUE; |
| 50 | 0 | } |
| 51 | | |
| 52 | | /** |
| 53 | | * Session handler assigned by session_set_save_handler(). |
| 54 | | * |
| 55 | | * This function will be called by PHP to retrieve the current user's |
| 56 | | * session data, which is stored in the database. It also loads the |
| 57 | | * current user's appropriate roles into the user object. |
| 58 | | * |
| 59 | | * This function should not be called directly. Session data should |
| 60 | | * instead be accessed via the $_SESSION superglobal. |
| 61 | | * |
| 62 | | * @param $key |
| 63 | | * Session ID. |
| 64 | | * @return |
| 65 | | * Either an array of the session data, or an empty string, if no data |
| 66 | | * was found or the user is anonymous. |
| 67 | | */ |
| 68 | 2367 | function _sess_read($key) { |
| 69 | 2367 | global $user; |
| 70 | | |
| 71 | | // Write and Close handlers are called after destructing objects |
| 72 | | // since PHP 5.0.5. |
| 73 | | // Thus destructors can use sessions but session handler can't use
objects. |
| 74 | | // So we are moving session closure before destructing objects. |
| 75 | 2367 | register_shutdown_function('session_write_close'); |
| 76 | | |
| 77 | | // Handle the case of first time visitors and clients that don't store |
| 78 | | // cookies (eg. web crawlers). |
| 79 | 2367 | if (!isset($_COOKIE[session_name()])) { |
| 80 | 134 | $user = drupal_anonymous_user(); |
| 81 | 134 | return ''; |
| 82 | 0 | } |
| 83 | | |
| 84 | | // Otherwise, if the session is still active, we have a record of the |
| 85 | | // client's session in the database. |
| 86 | 2233 | $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(); |
| 87 | | |
| 88 | | // We found the client's session record and they are an authenticated
user. |
| 89 | 2233 | if ($user && $user->uid > 0) { |
| 90 | | // This is done to unserialize the data member of $user. |
| 91 | 1594 | $user = drupal_unpack($user); |
| 92 | | |
| 93 | | // Add roles element to $user. |
| 94 | 1594 | $user->roles = array(); |
| 95 | 1594 | $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; |
| 96 | 1594 | $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); |
| 97 | 1594 | } |
| 98 | 1586 | // We didn't find the client's record (session has expired), or they |
| 99 | 1586 | // are an anonymous user. |
| 100 | 1594 | else { |
| 101 | | $session = isset($user->session) ? $user->session : ''; |
| 102 | | $user = drupal_anonymous_user($session); |
| 103 | | } |
| 104 | 639 | |
| 105 | 639 | return $user->session; |
| 106 | | } |
| 107 | | |
| 108 | 2233 | /** |
| 109 | 0 | * Session handler assigned by session_set_save_handler(). |
| 110 | | * |
| 111 | | * This function will be called by PHP to store the current user's |
| 112 | | * session, which Drupal saves to the database. |
| 113 | | * |
| 114 | | * This function should not be called directly. Session data should |
| 115 | | * instead be accessed via the $_SESSION superglobal. |
| 116 | | * |
| 117 | | * @param $key |
| 118 | | * Session ID. |
| 119 | | * @param $value |
| 120 | | * Serialized array of the session data. |
| 121 | | * @return |
| 122 | | * This function will always return TRUE. |
| 123 | | */ |
| 124 | | function _sess_write($key, $value) { |
| 125 | | global $user; |
| 126 | | |
| 127 | 2367 | // If saving of session data is disabled or if the client doesn't have a
session, |
| 128 | 483 | // and one isn't being created ($value), do nothing. This keeps crawlers
out of |
| 129 | | // the session table. This reduces memory and server load, and gives more
useful |
| 130 | | // statistics. We can't eliminate anonymous session table rows without
breaking |
| 131 | | // the "Who's Online" block. |
| 132 | | if (!drupal_save_session() || ($user->uid == 0 &&
empty($_COOKIE[session_name()]) && empty($value))) { |
| 133 | | return TRUE; |
| 134 | | } |
| 135 | 483 | |
| 136 | 0 | db_merge('sessions') |
| 137 | 0 | ->key(array('sid' => $key)) |
| 138 | | ->fields(array( |
| 139 | 483 | 'uid' => $user->uid, |
| 140 | | 'cache' => isset($user->cache) ? $user->cache : 0, |
| 141 | 483 | 'hostname' => ip_address(), |
| 142 | | 'session' => $value, |
| 143 | | 'timestamp' => REQUEST_TIME, |
| 144 | | )) |
| 145 | | ->execute(); |
| 146 | 1 | |
| 147 | 1 | // Last access time is updated no more frequently than once every 180
seconds. |
| 148 | 1 | // This reduces contention in the users table. |
| 149 | 1 | if ($user->uid && REQUEST_TIME - $user->access >
variable_get('session_write_interval', 180)) { |
| 150 | | db_update('users') |
| 151 | 482 | ->fields(array( |
| 152 | | 'access' => REQUEST_TIME |
| 153 | 482 | )) |
| 154 | | ->condition('uid', $user->uid) |
| 155 | | ->execute(); |
| 156 | | } |
| 157 | 482 | |
| 158 | 21 | return TRUE; |
| 159 | 21 | } |
| 160 | 482 | |
| 161 | | /** |
| 162 | | * Called when an anonymous user becomes authenticated or vice-versa. |
| 163 | 483 | */ |
| 164 | 0 | function drupal_session_regenerate() { |
| 165 | | $old_session_id = session_id(); |
| 166 | | session_regenerate_id(); |
| 167 | | db_update('sessions') |
| 168 | | ->fields(array( |
| 169 | 2367 | 'sid' => session_id() |
| 170 | 187 | )) |
| 171 | 187 | ->condition('sid', $old_session_id) |
| 172 | 187 | ->execute(); |
| 173 | 187 | } |
| 174 | | |
| 175 | | /** |
| 176 | | * Counts how many users are active on the site. |
| 177 | | * |
| 178 | | * Counts how many users have sessions which have been active since the |
| 179 | | * specified time. Can count either anonymous sessions or |
| 180 | | * authenticated sessions. |
| 181 | | * |
| 182 | | * @param int $timestamp. |
| 183 | | * A Unix timestamp. Users who have been active since this time will be |
| 184 | | * counted. The default is 0, which counts all existing sessions. |
| 185 | | * @param boolean $anonymous |
| 186 | | * TRUE counts only anonymous users. |
| 187 | | * FALSE counts only authenticated users. |
| 188 | | * @return int |
| 189 | | * The number of users with sessions. |
| 190 | | */ |
| 191 | 2367 | function drupal_session_count($timestamp = 0, $anonymous = TRUE) { |
| 192 | 1 | $query = db_select('sessions'); |
| 193 | 1 | $query->addExpression('COUNT(sid)', 'count'); |
| 194 | 0 | $query->condition('timestamp', $timestamp, '>='); |
| 195 | | $query->condition('uid', 0, $anonymous ? '=' : '>'); |
| 196 | | return $query->execute()->fetchField(); |
| 197 | | } |
| 198 | | |
| 199 | | /** |
| 200 | | * Session handler assigned by session_set_save_handler(). |
| 201 | | * |
| 202 | | * Cleanup a specific session. |
| 203 | | * |
| 204 | 2367 | * @param string $sid |
| 205 | 94 | * Session ID. |
| 206 | 94 | */ |
| 207 | | function _sess_destroy_sid($sid) { |
| 208 | | db_delete('sessions') |
| 209 | | ->condition('sid', $sid) |
| 210 | | ->execute(); |
| 211 | | } |
| 212 | | |
| 213 | | /** |
| 214 | 2367 | * End a specific user's session(s). |
| 215 | 4 | * |
| 216 | 4 | * @param string $uid |
| 217 | | * User ID. |
| 218 | | */ |
| 219 | | function drupal_session_destroy_uid($uid) { |
| 220 | | db_delete('sessions') |
| 221 | | ->condition('uid', $uid) |
| 222 | | ->execute(); |
| 223 | | } |
| 224 | | |
| 225 | | /** |
| 226 | | * Session handler assigned by session_set_save_handler(). |
| 227 | 2367 | * |
| 228 | | * Cleanup stalled sessions. |
| 229 | | * |
| 230 | | * @param int $lifetime |
| 231 | | * The value of session.gc_maxlifetime, passed by PHP. |
| 232 | | * Sessions not updated for more than $lifetime seconds will be removed.
|
| 233 | 0 | */ |
| 234 | | function _sess_gc($lifetime) { |
| 235 | 0 | // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough |
| 236 | 0 | // value. For example, if you want user sessions to stay in your database |
| 237 | | // for three weeks before deleting them, you need to set gc_maxlifetime |
| 238 | | // to '1814400'. At that value, only after a user doesn't log in after |
| 239 | | // three weeks (1814400 seconds) will his/her session be removed. |
| 240 | | db_delete('sessions') |
| 241 | | ->condition('timestamp', REQUEST_TIME - $lifetime, '<') |
| 242 | | ->execute(); |
| 243 | | return TRUE; |
| 244 | | } |
| 245 | | |
| 246 | | /** |
| 247 | | * Determine whether to save session data of the current request. |
| 248 | | * |
| 249 | | * This function allows the caller to temporarily disable writing of |
| 250 | | * session data, should the request end while performing potentially |
| 251 | | * dangerous operations, such as manipulating the global $user object. |
| 252 | 2367 | * See http://drupal.org/node/218104 for usage. |
| 253 | 487 | * |
| 254 | 487 | * @param $status |
| 255 | 4 | * Disables writing of session data when FALSE, (re-)enables |
| 256 | 4 | * writing when TRUE. |
| 257 | 487 | * @return |
| 258 | 0 | * FALSE if writing session data has been disabled. Otherwise, TRUE. |
| 259 | 2367 | */ |
| 260 | | function drupal_save_session($status = NULL) { |
| 261 | | static $save_session = TRUE; |
| 262 | | if (isset($status)) { |
| 263 | | $save_session = $status; |
| 264 | | } |
| 265 | | return $save_session; |
| 266 | | } |
| 267 | | |