Simpletest Coverage - includes/session.inc

1 <?php
2 // $Id: session.inc,v 1.70 2009/07/01 12:47:30 dries Exp $
3
4 /**
5 * @file
6 * User session handling functions.
7 *
8 * The user-level session storage handlers:
9 * - _drupal_session_open()
10 * - _drupal_session_close()
11 * - _drupal_session_read()
12 * - _drupal_session_write()
13 * - _drupal_session_destroy()
14 * - _drupal_session_garbage_collection()
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 function _drupal_session_open() {
33 return TRUE;
34 }
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 function _drupal_session_close() {
49 return TRUE;
50 }
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 $sid
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 function _drupal_session_read($sid) {
69 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 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 if (!isset($_COOKIE[session_name()])) {
80 $user = drupal_anonymous_user();
81 return '';
82 }
83
84 // Otherwise, if the session is still active, we have a record of the
85 // client's session in the database.
86 $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();
87
88 // We found the client's session record and they are an authenticated user.
89 if ($user && $user->uid > 0) {
90 // This is done to unserialize the data member of $user.
91 $user = drupal_unpack($user);
92
93 // Add roles element to $user.
94 $user->roles = array();
95 $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
96 $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 }
98 // We didn't find the client's record (session has expired), or they
99 // are an anonymous user.
100 else {
101 $session = isset($user->session) ? $user->session : '';
102 $user = drupal_anonymous_user($session);
103 }
104
105 return $user->session;
106 }
107
108 /**
109 * 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 $sid
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 _drupal_session_write($sid, $value) {
125 global $user;
126
127 if (!drupal_save_session()) {
128 // We don't have anything to do if we are not allowed to save the session.
129 return;
130 }
131
132 db_merge('sessions')
133 ->key(array('sid' => $sid))
134 ->fields(array(
135 'uid' => $user->uid,
136 'cache' => isset($user->cache) ? $user->cache : 0,
137 'hostname' => ip_address(),
138 'session' => $value,
139 'timestamp' => REQUEST_TIME,
140 ))
141 ->execute();
142
143 // Last access time is updated no more frequently than once every 180 seconds.
144 // This reduces contention in the users table.
145 if ($user->uid && REQUEST_TIME - $user->access > variable_get('session_write_interval', 180)) {
146 db_update('users')
147 ->fields(array(
148 'access' => REQUEST_TIME
149 ))
150 ->condition('uid', $user->uid)
151 ->execute();
152 }
153
154 return TRUE;
155 }
156
157 /**
158 * Initialize the session handler, starting a session if needed.
159 */
160 function drupal_session_initialize() {
161 global $user;
162
163 session_set_save_handler('_drupal_session_open', '_drupal_session_close', '_drupal_session_read', '_drupal_session_write', '_drupal_session_destroy', '_drupal_session_garbage_collection');
164
165 if (isset($_COOKIE[session_name()])) {
166 // If a session cookie exists, initialize the session. Otherwise the
167 // session is only started on demand in drupal_session_commit(), making
168 // anonymous users not use a session cookie unless something is stored in
169 // $_SESSION. This allows HTTP proxies to cache anonymous pageviews.
170 drupal_session_start();
171 if (!empty($user->uid) || !empty($_SESSION)) {
172 drupal_page_is_cacheable(FALSE);
173 }
174 }
175 else {
176 // Set a session identifier for this request. This is necessary because
177 // we lazyly start sessions at the end of this request, and some
178 // processes (like drupal_get_token()) needs to know the future
179 // session ID in advance.
180 $user = drupal_anonymous_user();
181 session_id(md5(uniqid('', TRUE)));
182 }
183 }
184
185 /**
186 * Forcefully start a session, preserving already set session data.
187 */
188 function drupal_session_start() {
189 if (!drupal_session_started()) {
190 // Save current session data before starting it, as PHP will destroy it.
191 $session_data = isset($_SESSION) ? $_SESSION : NULL;
192
193 session_start();
194 drupal_session_started(TRUE);
195
196 // Restore session data.
197 if (!empty($session_data)) {
198 $_SESSION += $session_data;
199 }
200 }
201 }
202
203 /**
204 * Commit the current session, if necessary.
205 *
206 * If an anonymous user already have an empty session, destroy it.
207 */
208 function drupal_session_commit() {
209 global $user;
210
211 if (!drupal_save_session()) {
212 // We don't have anything to do if we are not allowed to save the session.
213 return;
214 }
215
216 if (empty($user->uid) && empty($_SESSION)) {
217 // There is no session data to store, destroy the session if it was
218 // previously started.
219 if (drupal_session_started()) {
220 session_destroy();
221 }
222 }
223 else {
224 // There is session data to store. Start the session if it is not already
225 // started.
226 if (!drupal_session_started()) {
227 drupal_session_start();
228 }
229 // Write the session data.
230 session_write_close();
231 }
232 }
233
234 /**
235 * Return whether a session has been started.
236 */
237 function drupal_session_started($set = NULL) {
238 static $session_started = FALSE;
239 if (isset($set)) {
240 $session_started = $set;
241 }
242 return $session_started && session_id();
243 }
244
245 /**
246 * Called when an anonymous user becomes authenticated or vice-versa.
247 */
248 function drupal_session_regenerate() {
249 global $user;
250
251 if (drupal_session_started()) {
252 $old_session_id = session_id();
253 session_regenerate_id();
254 }
255 else {
256 // Start the session when it doesn't exist yet.
257 // Preserve the logged in user, as it will be reset to anonymous
258 // by _drupal_session_read.
259 $account = $user;
260 drupal_session_start();
261 $user = $account;
262 }
263
264 if (isset($old_session_id)) {
265 db_update('sessions')
266 ->fields(array(
267 'sid' => session_id()
268 ))
269 ->condition('sid', $old_session_id)
270 ->execute();
271 }
272 }
273
274 /**
275 * Counts how many users are active on the site.
276 *
277 * Counts how many users have sessions which have been active since the
278 * specified time. Can count either anonymous sessions or
279 * authenticated sessions.
280 *
281 * @param int $timestamp.
282 * A Unix timestamp. Users who have been active since this time will be
283 * counted. The default is 0, which counts all existing sessions.
284 * @param boolean $anonymous
285 * TRUE counts only anonymous users.
286 * FALSE counts only authenticated users.
287 * @return int
288 * The number of users with sessions.
289 */
290 function drupal_session_count($timestamp = 0, $anonymous = TRUE) {
291 $query = db_select('sessions');
292 $query->addExpression('COUNT(sid)', 'count');
293 $query->condition('timestamp', $timestamp, '>=');
294 $query->condition('uid', 0, $anonymous ? '=' : '>');
295 return $query->execute()->fetchField();
296 }
297
298 /**
299 * Session handler assigned by session_set_save_handler().
300 *
301 * Cleanup a specific session.
302 *
303 * @param string $sid
304 * Session ID.
305 */
306 function _drupal_session_destroy($sid) {
307 global $user;
308
309 // Delete session data.
310 db_delete('sessions')
311 ->condition('sid', $sid)
312 ->execute();
313
314 // Reset $_SESSION and $user to prevent a new session from being started
315 // in drupal_session_commit().
316 $_SESSION = array();
317 $user = drupal_anonymous_user();
318
319 // Unset the session cookie.
320 if (isset($_COOKIE[session_name()])) {
321 $params = session_get_cookie_params();
322 setcookie(session_name(), '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
323 unset($_COOKIE[session_name()]);
324 }
325 }
326
327 /**
328 * End a specific user's session(s).
329 *
330 * @param string $uid
331 * User ID.
332 */
333 function drupal_session_destroy_uid($uid) {
334 db_delete('sessions')
335 ->condition('uid', $uid)
336 ->execute();
337 }
338
339 /**
340 * Session handler assigned by session_set_save_handler().
341 *
342 * Cleanup stalled sessions.
343 *
344 * @param int $lifetime
345 * The value of session.gc_maxlifetime, passed by PHP.
346 * Sessions not updated for more than $lifetime seconds will be removed.
347 */
348 function _drupal_session_garbage_collection($lifetime) {
349 // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough
350 // value. For example, if you want user sessions to stay in your database
351 // for three weeks before deleting them, you need to set gc_maxlifetime
352 // to '1814400'. At that value, only after a user doesn't log in after
353 // three weeks (1814400 seconds) will his/her session be removed.
354 db_delete('sessions')
355 ->condition('timestamp', REQUEST_TIME - $lifetime, '<')
356 ->execute();
357 return TRUE;
358 }
359
360 /**
361 * Determine whether to save session data of the current request.
362 *
363 * This function allows the caller to temporarily disable writing of
364 * session data, should the request end while performing potentially
365 * dangerous operations, such as manipulating the global $user object.
366 * See http://drupal.org/node/218104 for usage.
367 *
368 * @param $status
369 * Disables writing of session data when FALSE, (re-)enables
370 * writing when TRUE.
371 * @return
372 * FALSE if writing session data has been disabled. Otherwise, TRUE.
373 */
374 function drupal_save_session($status = NULL) {
375 $save_session = &drupal_static(__FUNCTION__, TRUE);
376 if (isset($status)) {
377 $save_session = $status;
378 }
379 return $save_session;
380 }
381

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.