session.inc

  1. drupal
    1. 4.6 includes/session.inc
    2. 4.7 includes/session.inc
    3. 5 includes/session.inc
    4. 6 includes/session.inc
    5. 7 includes/session.inc
    6. 8 core/includes/session.inc

User session handling functions.

The user-level session storage handlers:

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.

Functions & methods

NameDescription
drupal_save_sessionDetermines whether to save session data of the current request.
drupal_session_commitCommits the current session, if necessary.
drupal_session_destroy_uidEnds a specific user's session(s).
drupal_session_initializeInitializes the session handler, starting a session if needed.
drupal_session_regenerateCalled when an anonymous user becomes authenticated or vice-versa.
drupal_session_startForcefully starts a session, preserving already set session data.
drupal_session_startedReturns whether a session has been started.
_drupal_session_closeSession handler assigned by session_set_save_handler().
_drupal_session_delete_cookieDeletes the session cookie.
_drupal_session_destroySession handler assigned by session_set_save_handler().
_drupal_session_garbage_collectionSession handler assigned by session_set_save_handler().
_drupal_session_openSession handler assigned by session_set_save_handler().
_drupal_session_readReads an entire session from the database (internal use only).
_drupal_session_writeWrites an entire session to the database (internal use only).

File

core/includes/session.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * User session handling functions.
  5. *
  6. * The user-level session storage handlers:
  7. * - _drupal_session_open()
  8. * - _drupal_session_close()
  9. * - _drupal_session_read()
  10. * - _drupal_session_write()
  11. * - _drupal_session_destroy()
  12. * - _drupal_session_garbage_collection()
  13. * are assigned by session_set_save_handler() in bootstrap.inc and are called
  14. * automatically by PHP. These functions should not be called directly. Session
  15. * data should instead be accessed via the $_SESSION superglobal.
  16. */
  17. /**
  18. * Session handler assigned by session_set_save_handler().
  19. *
  20. * This function is used to handle any initialization, such as file paths or
  21. * database connections, that is needed before accessing session data. Drupal
  22. * does not need to initialize anything in this function.
  23. *
  24. * This function should not be called directly.
  25. *
  26. * @return
  27. * This function will always return TRUE.
  28. */
  29. function _drupal_session_open() {
  30. return TRUE;
  31. }
  32. /**
  33. * Session handler assigned by session_set_save_handler().
  34. *
  35. * This function is used to close the current session. Because Drupal stores
  36. * session data in the database immediately on write, this function does
  37. * not need to do anything.
  38. *
  39. * This function should not be called directly.
  40. *
  41. * @return
  42. * This function will always return TRUE.
  43. */
  44. function _drupal_session_close() {
  45. return TRUE;
  46. }
  47. /**
  48. * Reads an entire session from the database (internal use only).
  49. *
  50. * Also initializes the $user object for the user associated with the session.
  51. * This function is registered with session_set_save_handler() to support
  52. * database-backed sessions. It is called on every page load when PHP sets
  53. * up the $_SESSION superglobal.
  54. *
  55. * This function is an internal function and must not be called directly.
  56. * Doing so may result in logging out the current user, corrupting session data
  57. * or other unexpected behavior. Session data must always be accessed via the
  58. * $_SESSION superglobal.
  59. *
  60. * @param $sid
  61. * The session ID of the session to retrieve.
  62. *
  63. * @return
  64. * The user's session, or an empty string if no session exists.
  65. */
  66. function _drupal_session_read($sid) {
  67. global $user, $is_https;
  68. // Write and Close handlers are called after destructing objects
  69. // since PHP 5.0.5.
  70. // Thus destructors can use sessions but session handler can't use objects.
  71. // So we are moving session closure before destructing objects.
  72. drupal_register_shutdown_function('session_write_close');
  73. // Handle the case of first time visitors and clients that don't store
  74. // cookies (eg. web crawlers).
  75. $insecure_session_name = substr(session_name(), 1);
  76. if (!isset($_COOKIE[session_name()]) && !isset($_COOKIE[$insecure_session_name])) {
  77. $user = drupal_anonymous_user();
  78. return '';
  79. }
  80. // Otherwise, if the session is still active, we have a record of the
  81. // client's session in the database. If it's HTTPS then we are either have
  82. // a HTTPS session or we are about to log in so we check the sessions table
  83. // for an anonymous session with the non-HTTPS-only cookie.
  84. if ($is_https) {
  85. $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.ssid = :ssid", array(':ssid' => $sid))->fetchObject();
  86. if (!$user) {
  87. if (isset($_COOKIE[$insecure_session_name])) {
  88. $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid AND s.uid = 0", array(
  89. ':sid' => $_COOKIE[$insecure_session_name]))
  90. ->fetchObject();
  91. }
  92. }
  93. }
  94. else {
  95. $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();
  96. }
  97. // We found the client's session record and they are an authenticated,
  98. // active user.
  99. if ($user && $user->uid > 0 && $user->status == 1) {
  100. // This is done to unserialize the data member of $user.
  101. $user->data = unserialize($user->data);
  102. // Add roles element to $user.
  103. $user->roles = array();
  104. $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
  105. $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);
  106. }
  107. elseif ($user) {
  108. // The user is anonymous or blocked. Only preserve two fields from the
  109. // {sessions} table.
  110. $account = drupal_anonymous_user();
  111. $account->session = $user->session;
  112. $account->timestamp = $user->timestamp;
  113. $user = $account;
  114. }
  115. else {
  116. // The session has expired.
  117. $user = drupal_anonymous_user();
  118. $user->session = '';
  119. }
  120. // Store the session that was read for comparison in _drupal_session_write().
  121. $last_read = &drupal_static('drupal_session_last_read');
  122. $last_read = array(
  123. 'sid' => $sid,
  124. 'value' => $user->session,
  125. );
  126. return $user->session;
  127. }
  128. /**
  129. * Writes an entire session to the database (internal use only).
  130. *
  131. * This function is registered with session_set_save_handler() to support
  132. * database-backed sessions.
  133. *
  134. * This function is an internal function and must not be called directly.
  135. * Doing so may result in corrupted session data or other unexpected behavior.
  136. * Session data must always be accessed via the $_SESSION superglobal.
  137. *
  138. * @param $sid
  139. * The session ID of the session to write to.
  140. * @param $value
  141. * Session data to write as a serialized string.
  142. *
  143. * @return
  144. * Always returns TRUE.
  145. */
  146. function _drupal_session_write($sid, $value) {
  147. global $user, $is_https;
  148. // The exception handler is not active at this point, so we need to do it
  149. // manually.
  150. try {
  151. if (!drupal_save_session()) {
  152. // We don't have anything to do if we are not allowed to save the session.
  153. return;
  154. }
  155. // Check whether $_SESSION has been changed in this request.
  156. $last_read = &drupal_static('drupal_session_last_read');
  157. $is_changed = !isset($last_read) || $last_read['sid'] != $sid || $last_read['value'] !== $value;
  158. // For performance reasons, do not update the sessions table, unless
  159. // $_SESSION has changed or more than 180 has passed since the last update.
  160. if ($is_changed || !isset($user->timestamp) || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) {
  161. // Either ssid or sid or both will be added from $key below.
  162. $fields = array(
  163. 'uid' => $user->uid,
  164. 'hostname' => ip_address(),
  165. 'session' => $value,
  166. 'timestamp' => REQUEST_TIME,
  167. );
  168. // Use the session ID as 'sid' and an empty string as 'ssid' by default.
  169. // _drupal_session_read() does not allow empty strings so that's a safe
  170. // default.
  171. $key = array('sid' => $sid, 'ssid' => '');
  172. // On HTTPS connections, use the session ID as both 'sid' and 'ssid'.
  173. if ($is_https) {
  174. $key['ssid'] = $sid;
  175. // The "secure pages" setting allows a site to simultaneously use both
  176. // secure and insecure session cookies. If enabled and both cookies are
  177. // presented then use both keys.
  178. if (variable_get('https', FALSE)) {
  179. $insecure_session_name = substr(session_name(), 1);
  180. if (isset($_COOKIE[$insecure_session_name])) {
  181. $key['sid'] = $_COOKIE[$insecure_session_name];
  182. }
  183. }
  184. }
  185. elseif (variable_get('https', FALSE)) {
  186. unset($key['ssid']);
  187. }
  188. db_merge('sessions')
  189. ->key($key)
  190. ->fields($fields)
  191. ->execute();
  192. }
  193. // Likewise, do not update access time more than once per 180 seconds.
  194. if ($user->uid && REQUEST_TIME - $user->access > variable_get('session_write_interval', 180)) {
  195. db_update('users')
  196. ->fields(array(
  197. 'access' => REQUEST_TIME
  198. ))
  199. ->condition('uid', $user->uid)
  200. ->execute();
  201. }
  202. return TRUE;
  203. }
  204. catch (Exception $exception) {
  205. require_once DRUPAL_ROOT . '/core/includes/errors.inc';
  206. // If we are displaying errors, then do so with no possibility of a further
  207. // uncaught exception being thrown.
  208. if (error_displayable()) {
  209. print '<h1>Uncaught exception thrown in session handler.</h1>';
  210. print '<p>' . _drupal_render_exception_safe($exception) . '</p><hr />';
  211. }
  212. return FALSE;
  213. }
  214. }
  215. /**
  216. * Initializes the session handler, starting a session if needed.
  217. */
  218. function drupal_session_initialize() {
  219. global $user, $is_https;
  220. session_set_save_handler('_drupal_session_open', '_drupal_session_close', '_drupal_session_read', '_drupal_session_write', '_drupal_session_destroy', '_drupal_session_garbage_collection');
  221. // We use !empty() in the following check to ensure that blank session IDs
  222. // are not valid.
  223. if (!empty($_COOKIE[session_name()]) || ($is_https && variable_get('https', FALSE) && !empty($_COOKIE[substr(session_name(), 1)]))) {
  224. // If a session cookie exists, initialize the session. Otherwise the
  225. // session is only started on demand in drupal_session_commit(), making
  226. // anonymous users not use a session cookie unless something is stored in
  227. // $_SESSION. This allows HTTP proxies to cache anonymous pageviews.
  228. drupal_session_start();
  229. if (!empty($user->uid) || !empty($_SESSION)) {
  230. drupal_page_is_cacheable(FALSE);
  231. }
  232. }
  233. else {
  234. // Set a session identifier for this request. This is necessary because
  235. // we lazily start sessions at the end of this request, and some
  236. // processes (like drupal_get_token()) needs to know the future
  237. // session ID in advance.
  238. $GLOBALS['lazy_session'] = TRUE;
  239. $user = drupal_anonymous_user();
  240. // Less random sessions (which are much faster to generate) are used for
  241. // anonymous users than are generated in drupal_session_regenerate() when
  242. // a user becomes authenticated.
  243. session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE)));
  244. if ($is_https && variable_get('https', FALSE)) {
  245. $insecure_session_name = substr(session_name(), 1);
  246. $session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE));
  247. $_COOKIE[$insecure_session_name] = $session_id;
  248. }
  249. }
  250. date_default_timezone_set(drupal_get_user_timezone());
  251. }
  252. /**
  253. * Forcefully starts a session, preserving already set session data.
  254. *
  255. * @ingroup php_wrappers
  256. */
  257. function drupal_session_start() {
  258. // Command line clients do not support cookies nor sessions.
  259. if (!drupal_session_started() && !drupal_is_cli()) {
  260. // Save current session data before starting it, as PHP will destroy it.
  261. $session_data = isset($_SESSION) ? $_SESSION : NULL;
  262. session_start();
  263. drupal_session_started(TRUE);
  264. // Restore session data.
  265. if (!empty($session_data)) {
  266. $_SESSION += $session_data;
  267. }
  268. }
  269. }
  270. /**
  271. * Commits the current session, if necessary.
  272. *
  273. * If an anonymous user already have an empty session, destroy it.
  274. */
  275. function drupal_session_commit() {
  276. global $user, $is_https;
  277. if (!drupal_save_session()) {
  278. // We don't have anything to do if we are not allowed to save the session.
  279. return;
  280. }
  281. if (empty($user->uid) && empty($_SESSION)) {
  282. // There is no session data to store, destroy the session if it was
  283. // previously started.
  284. if (drupal_session_started()) {
  285. session_destroy();
  286. }
  287. }
  288. else {
  289. // There is session data to store. Start the session if it is not already
  290. // started.
  291. if (!drupal_session_started()) {
  292. drupal_session_start();
  293. if ($is_https && variable_get('https', FALSE)) {
  294. $insecure_session_name = substr(session_name(), 1);
  295. $params = session_get_cookie_params();
  296. $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
  297. setcookie($insecure_session_name, $_COOKIE[$insecure_session_name], $expire, $params['path'], $params['domain'], FALSE, $params['httponly']);
  298. }
  299. }
  300. // Write the session data.
  301. session_write_close();
  302. }
  303. }
  304. /**
  305. * Returns whether a session has been started.
  306. */
  307. function drupal_session_started($set = NULL) {
  308. static $session_started = FALSE;
  309. if (isset($set)) {
  310. $session_started = $set;
  311. }
  312. return $session_started && session_id();
  313. }
  314. /**
  315. * Called when an anonymous user becomes authenticated or vice-versa.
  316. *
  317. * @ingroup php_wrappers
  318. */
  319. function drupal_session_regenerate() {
  320. global $user, $is_https;
  321. if ($is_https && variable_get('https', FALSE)) {
  322. $insecure_session_name = substr(session_name(), 1);
  323. if (!isset($GLOBALS['lazy_session']) && isset($_COOKIE[$insecure_session_name])) {
  324. $old_insecure_session_id = $_COOKIE[$insecure_session_name];
  325. }
  326. $params = session_get_cookie_params();
  327. $session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
  328. // If a session cookie lifetime is set, the session will expire
  329. // $params['lifetime'] seconds from the current request. If it is not set,
  330. // it will expire when the browser is closed.
  331. $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
  332. setcookie($insecure_session_name, $session_id, $expire, $params['path'], $params['domain'], FALSE, $params['httponly']);
  333. $_COOKIE[$insecure_session_name] = $session_id;
  334. }
  335. if (drupal_session_started()) {
  336. $old_session_id = session_id();
  337. }
  338. session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55)));
  339. if (isset($old_session_id)) {
  340. $params = session_get_cookie_params();
  341. $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
  342. setcookie(session_name(), session_id(), $expire, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
  343. $fields = array('sid' => session_id());
  344. if ($is_https) {
  345. $fields['ssid'] = session_id();
  346. // If the "secure pages" setting is enabled, use the newly-created
  347. // insecure session identifier as the regenerated sid.
  348. if (variable_get('https', FALSE)) {
  349. $fields['sid'] = $session_id;
  350. }
  351. }
  352. db_update('sessions')
  353. ->fields($fields)
  354. ->condition($is_https ? 'ssid' : 'sid', $old_session_id)
  355. ->execute();
  356. }
  357. elseif (isset($old_insecure_session_id)) {
  358. // If logging in to the secure site, and there was no active session on the
  359. // secure site but a session was active on the insecure site, update the
  360. // insecure session with the new session identifiers.
  361. db_update('sessions')
  362. ->fields(array('sid' => $session_id, 'ssid' => session_id()))
  363. ->condition('sid', $old_insecure_session_id)
  364. ->execute();
  365. }
  366. else {
  367. // Start the session when it doesn't exist yet.
  368. // Preserve the logged in user, as it will be reset to anonymous
  369. // by _drupal_session_read.
  370. $account = $user;
  371. drupal_session_start();
  372. $user = $account;
  373. }
  374. date_default_timezone_set(drupal_get_user_timezone());
  375. }
  376. /**
  377. * Session handler assigned by session_set_save_handler().
  378. *
  379. * Cleans up a specific session.
  380. *
  381. * @param $sid
  382. * Session ID.
  383. */
  384. function _drupal_session_destroy($sid) {
  385. global $user, $is_https;
  386. // Delete session data.
  387. db_delete('sessions')
  388. ->condition($is_https ? 'ssid' : 'sid', $sid)
  389. ->execute();
  390. // Reset $_SESSION and $user to prevent a new session from being started
  391. // in drupal_session_commit().
  392. $_SESSION = array();
  393. $user = drupal_anonymous_user();
  394. // Unset the session cookies.
  395. _drupal_session_delete_cookie(session_name());
  396. if ($is_https) {
  397. _drupal_session_delete_cookie(substr(session_name(), 1), FALSE);
  398. }
  399. elseif (variable_get('https', FALSE)) {
  400. _drupal_session_delete_cookie('S' . session_name(), TRUE);
  401. }
  402. }
  403. /**
  404. * Deletes the session cookie.
  405. *
  406. * @param $name
  407. * Name of session cookie to delete.
  408. * @param boolean $secure
  409. * Force the secure value of the cookie.
  410. */
  411. function _drupal_session_delete_cookie($name, $secure = NULL) {
  412. global $is_https;
  413. if (isset($_COOKIE[$name]) || (!$is_https && $secure === TRUE)) {
  414. $params = session_get_cookie_params();
  415. if ($secure !== NULL) {
  416. $params['secure'] = $secure;
  417. }
  418. setcookie($name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
  419. unset($_COOKIE[$name]);
  420. }
  421. }
  422. /**
  423. * Ends a specific user's session(s).
  424. *
  425. * @param $uid
  426. * User ID.
  427. */
  428. function drupal_session_destroy_uid($uid) {
  429. db_delete('sessions')
  430. ->condition('uid', $uid)
  431. ->execute();
  432. }
  433. /**
  434. * Session handler assigned by session_set_save_handler().
  435. *
  436. * Cleans up stalled sessions.
  437. *
  438. * @param $lifetime
  439. * The value of session.gc_maxlifetime, passed by PHP.
  440. * Sessions not updated for more than $lifetime seconds will be removed.
  441. */
  442. function _drupal_session_garbage_collection($lifetime) {
  443. // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough
  444. // value. For example, if you want user sessions to stay in your database
  445. // for three weeks before deleting them, you need to set gc_maxlifetime
  446. // to '1814400'. At that value, only after a user doesn't log in after
  447. // three weeks (1814400 seconds) will his/her session be removed.
  448. db_delete('sessions')
  449. ->condition('timestamp', REQUEST_TIME - $lifetime, '<')
  450. ->execute();
  451. return TRUE;
  452. }
  453. /**
  454. * Determines whether to save session data of the current request.
  455. *
  456. * This function allows the caller to temporarily disable writing of
  457. * session data, should the request end while performing potentially
  458. * dangerous operations, such as manipulating the global $user object.
  459. * See http://drupal.org/node/218104 for usage.
  460. *
  461. * @param $status
  462. * Disables writing of session data when FALSE, (re-)enables
  463. * writing when TRUE.
  464. *
  465. * @return
  466. * FALSE if writing session data has been disabled. Otherwise, TRUE.
  467. */
  468. function drupal_save_session($status = NULL) {
  469. $save_session = &drupal_static(__FUNCTION__, TRUE);
  470. if (isset($status)) {
  471. $save_session = $status;
  472. }
  473. return $save_session;
  474. }
Login or register to post comments