diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php index 76aa83b..1c357e6 100644 --- a/core/lib/Drupal/Core/Session/SessionManager.php +++ b/core/lib/Drupal/Core/Session/SessionManager.php @@ -2,33 +2,91 @@ /** * @file - * User session handling functions. + * Contains \Drupal\Core\Session\SessionManager. */ +namespace Drupal\Core\Session; + use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Settings; +use Drupal\Core\Database\Connection; use Drupal\Core\Session\AnonymousUserSession; use Drupal\Core\Session\SessionHandler; +use Symfony\Component\HttpFoundation\Request; /** + * Manages user sessions. + */ +class SessionManager { + + /** + * The master request of this process. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; + + /** + * The database connection to use. + * + * @var \Drupal\Core\Database\Connection + */ + protected $connection; + + /** + * Whether a lazy session has been started. + * + * @var bool + */ + protected $lazySession; + + /** + * Whether session management is enabled or temporarily disabled. + * + * PHP session ID, session, and cookie handling happens in the global scope. + * This value has to persist, since a potentially wrong or disallowed session + * would be written otherwise. + * + * @var bool + */ + protected static $enabled = TRUE; + + /** + * Whether the session has been started. + * + * @var bool + */ + protected static $started = FALSE; + + /** + * Construct the session storage. + */ + public function __construct(Request $request, Connection $connection) { + $this->request = $request; + $this->connection = $connection; + } + + /** * Initializes the session handler, starting a session if needed. + * + * @return $this */ -function drupal_session_initialize() { + public function initialize() { global $user; // Register the default session handler. - // @todo: Extract session storage from session handler into a service. - $handler = new SessionHandler(\Drupal::request(), \Drupal::database()); + // @todo Extract session storage from session handler into a service. + $handler = new SessionHandler($this, $this->request, $this->connection); session_set_save_handler($handler, TRUE); - $is_https = \Drupal::request()->isSecure(); - $cookies = \Drupal::request()->cookies; + $is_https = $this->request->isSecure(); + $cookies = $this->request->cookies; if (($cookies->has(session_name()) && ($session_name = $cookies->get(session_name()))) || ($is_https && Settings::get('mixed_mode_sessions', FALSE) && ($cookies->has(substr(session_name(), 1))) && ($session_name = $cookies->get(substr(session_name(), 1))))) { // If a session cookie exists, initialize the session. Otherwise the - // session is only started on demand in drupal_session_commit(), making + // session is only started on demand in save(), making // anonymous users not use a session cookie unless something is stored in // $_SESSION. This allows HTTP proxies to cache anonymous pageviews. - drupal_session_start(); + $this->start(); if ($user->isAuthenticated() || !empty($_SESSION)) { drupal_page_is_cacheable(FALSE); } @@ -38,10 +96,10 @@ function drupal_session_initialize() { // we lazily start sessions at the end of this request, and some // processes (like drupal_get_token()) needs to know the future // session ID in advance. - $GLOBALS['lazy_session'] = TRUE; + $this->lazySession = TRUE; $user = new AnonymousUserSession(); // Less random sessions (which are much faster to generate) are used for - // anonymous users than are generated in drupal_session_regenerate() when + // anonymous users than are generated in migrate() when // a user becomes authenticated. session_id(Crypt::randomBytesBase64()); if ($is_https && Settings::get('mixed_mode_sessions', FALSE)) { @@ -51,38 +109,38 @@ function drupal_session_initialize() { } } date_default_timezone_set(drupal_get_user_timezone()); -} -/** + return $this; + } + + /** * Starts a session forcefully, preserving already set session data. - * - * @ingroup php_wrappers */ -function drupal_session_start() { - // Command line clients do not support cookies nor sessions. - if (!drupal_session_started() && !drupal_is_cli()) { + public function start() { + if ($this->isCli() || $this->isStarted()) { + return; + } // Save current session data before starting it, as PHP will destroy it. $session_data = isset($_SESSION) ? $_SESSION : NULL; session_start(); - drupal_session_started(TRUE); + static::$started = TRUE; // Restore session data. if (!empty($session_data)) { $_SESSION += $session_data; } } -} -/** + /** * Commits the current session, if necessary. * * If an anonymous user already have an empty session, destroy it. */ -function drupal_session_commit() { + public function save() { global $user; - if (!drupal_save_session()) { + if (!$this->isEnabled()) { // We don't have anything to do if we are not allowed to save the session. return; } @@ -90,58 +148,52 @@ function drupal_session_commit() { if ($user->isAnonymous() && empty($_SESSION)) { // There is no session data to store, destroy the session if it was // previously started. - if (drupal_session_started()) { + if ($this->isStarted()) { session_destroy(); } } else { // There is session data to store. Start the session if it is not already // started. - if (!drupal_session_started()) { - drupal_session_start(); - if (\Drupal::request()->isSecure() && Settings::get('mixed_mode_sessions', FALSE)) { + if (!$this->isStarted()) { + $this->start(); + if ($this->request->isSecure() && Settings::get('mixed_mode_sessions', FALSE)) { $insecure_session_name = substr(session_name(), 1); $params = session_get_cookie_params(); $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0; - $cookie_params = \Drupal::request()->cookies; + $cookie_params = $this->request->cookies; setcookie($insecure_session_name, $cookie_params->get($insecure_session_name), $expire, $params['path'], $params['domain'], FALSE, $params['httponly']); } } // Write the session data. session_write_close(); } -} + } -/** + /** * Returns whether a session has been started. */ -function drupal_session_started($set = NULL) { - static $session_started = FALSE; - if (isset($set)) { - $session_started = $set; + public function isStarted() { + return static::$started && session_status() === \PHP_SESSION_ACTIVE; } - return $session_started && session_status() === \PHP_SESSION_ACTIVE; -} -/** + /** * Called when an anonymous user becomes authenticated or vice-versa. - * - * @ingroup php_wrappers */ -function drupal_session_regenerate() { + public function migrate() { global $user; // Nothing to do if we are not allowed to change the session. - if (!drupal_save_session()) { + if (!$this->isEnabled()) { return; } - $is_https = \Drupal::request()->isSecure(); - $cookies = \Drupal::request()->cookies; + $is_https = $this->request->isSecure(); + $cookies = $this->request->cookies; if ($is_https && Settings::get('mixed_mode_sessions', FALSE)) { $insecure_session_name = substr(session_name(), 1); - if (!isset($GLOBALS['lazy_session']) && $cookies->has($insecure_session_name)) { + if (!isset($this->lazySession) && $cookies->has($insecure_session_name)) { $old_insecure_session_id = $cookies->get($insecure_session_name); } $params = session_get_cookie_params(); @@ -154,7 +206,7 @@ function drupal_session_regenerate() { $cookies->set($insecure_session_name, $session_id); } - if (drupal_session_started()) { + if ($this->isStarted()) { $old_session_id = session_id(); } session_id(Crypt::randomBytesBase64()); @@ -172,16 +224,16 @@ function drupal_session_regenerate() { $fields['sid'] = Crypt::hashBase64($session_id); } } - db_update('sessions') + $this->connection->update('sessions') ->fields($fields) ->condition($is_https ? 'ssid' : 'sid', Crypt::hashBase64($old_session_id)) ->execute(); } elseif (isset($old_insecure_session_id)) { - // If logging in to the secure site, and there was no active session on the - // secure site but a session was active on the insecure site, update the - // insecure session with the new session identifiers. - db_update('sessions') + // If logging in to the secure site, and there was no active session on + // the secure site but a session was active on the insecure site, update + // the insecure session with the new session identifiers. + $this->connection->update('sessions') ->fields(array('sid' => Crypt::hashBase64($session_id), 'ssid' => Crypt::hashBase64(session_id()))) ->condition('sid', Crypt::hashBase64($old_insecure_session_id)) ->execute(); @@ -189,53 +241,75 @@ function drupal_session_regenerate() { else { // Start the session when it doesn't exist yet. // Preserve the logged in user, as it will be reset to anonymous - // by _drupal_session_read. + // by \Drupal\Core\Session\SessionHandler::read(). $account = $user; - drupal_session_start(); + $this->start(); $user = $account; } date_default_timezone_set(drupal_get_user_timezone()); -} + } -/** + /** * Ends a specific user's session(s). * - * @param $uid + * @param int $uid * User ID. */ -function drupal_session_destroy_uid($uid) { + public function delete($uid) { // Nothing to do if we are not allowed to change the session. - if (!drupal_save_session()) { + if (!$this->isEnabled()) { return; } - - db_delete('sessions') + $this->connection->delete('sessions') ->condition('uid', $uid) ->execute(); -} + } -/** + /** * Determines whether to save session data of the current request. * + * @return bool + * FALSE if writing session data has been disabled. TRUE otherwise. + */ + public function isEnabled() { + return static::$enabled; + } + + /** + * Temporarily disables saving of session data. + * * 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. + * @see https://drupal.org/node/218104 + * + * @return $this + */ + public function disable() { + static::$enabled = FALSE; + return $this; + } + + /** + * Re-enables saving of session data. + * + * @return $this + */ + public function enable() { + static::$enabled = TRUE; + return $this; + } + + /** + * Returns whether the current PHP process runs on CLI. + * + * Command line clients do not support cookies nor sessions. * - * @return - * FALSE if writing session data has been disabled. Otherwise, TRUE. + * @return bool */ -function drupal_save_session($status = NULL) { - // PHP session ID, session, and cookie handling happens in the global scope. - // This value has to persist across calls to drupal_static_reset(), since a - // potentially wrong or disallowed session would be written otherwise. - static $save_session = TRUE; - if (isset($status)) { - $save_session = $status; + protected function isCli() { + return PHP_SAPI === 'cli'; } - return $save_session; + }