You are here

session_cache.module in Session Cache API 7

Same filename and directory in other branches
  1. 8 session_cache.module
  2. 6 session_cache.module

session_cache.module

A pluggable user session access facility for programmers who want a simple two-function API that is independent of the actual storage mechanism. Used in particular to avoid $_SESSION as the storage mechanism, so that anonymous user sessions can be used in the context of Varnish.

The database storage mechanism uses core's cache.inc.

See README.txt for details.

File

session_cache.module
View source
<?php

/**
 * @file
 * session_cache.module
 *
 * A pluggable user session access facility for programmers who want a simple
 * two-function API that is independent of the actual storage mechanism.
 * Used in particular to avoid $_SESSION as the storage mechanism, so that
 * anonymous user sessions can be used in the context of Varnish.
 *
 * The database storage mechanism uses core's cache.inc.
 *
 * See README.txt for details.
 */
define('SESSION_CACHE_STORAGE_COOKIE', 3);
define('SESSION_CACHE_STORAGE_DB_CORE', 2);
define('SESSION_CACHE_STORAGE_SESSION', 1);
define('SESSION_CACHE_COOKIE_FOR_SID', 0);
define('SESSION_CACHE_UID_FOR_SID', 1);
define('SESSION_CACHE_IP_ADDRESS_FOR_SID', 2);
define('SESSION_CACHE_DEFAULT_EXPIRATION_DAYS', 7);

// See [#2119579]
require_once DRUPAL_ROOT . '/includes/unicode.inc';

/**
 * Write data to the user session, whatever the storage mechanism may be.
 *
 * @param string $bin
 *   Unique id, eg a string prefixed by the module name.
 * @param mixed $data
 *   A number or string, an object, a multi-dimensional array etc.
 *   $bin is the identifier you choose for the data you want to store. To
 *   guarante uniqueness, you could prefix it with the name of the module that
 *   you use this API with.
 *   Use NULL to erase the bin; it may be recreated and refilled at any time by
 *   calling the function with a non-NULL data argument.
 */
function session_cache_set($bin, $data) {
  if (!isset($bin)) {
    return;
  }
  $method = variable_get('session_cache_storage_method', SESSION_CACHE_STORAGE_SESSION);
  switch ($method) {
    case SESSION_CACHE_STORAGE_COOKIE:
      $serialized_data = $data == NULL ? NULL : json_encode($data);

      // For $cookie_domain see also: drupal_settings_initialize()
      global $cookie_domain;

      // .localhost causes problems
      $cdom = $cookie_domain == '.localhost' ? ini_get('session.cookie_domain') : $cookie_domain;

      // Tell the browser to return the cookie only via HTTP or HTTPS, to any
      // path under the root of this server.
      setcookie("Drupal.session_cache.{$bin}", $serialized_data, $data == NULL ? 0 : session_cache_expiration_time(), '/', $cdom, NULL, TRUE);

      // Make sure that subsequent session_cache_get() calls get the data
      // updated here, not what was in the cookie at the start of the request!
      // Note that in array indices '.' are converted to '_'
      $_COOKIE["Drupal_session_cache_{$bin}"] = $serialized_data;
      return;
    case SESSION_CACHE_STORAGE_DB_CORE:
      $sid = session_cache_get_sid();
      if ($data == NULL) {
        cache_clear_all("{$bin}:{$sid}", 'cache_session_cache');
      }
      else {
        cache_set("{$bin}:{$sid}", $data, 'cache_session_cache', session_cache_expiration_time());
      }
      return;
    case SESSION_CACHE_STORAGE_SESSION:

      // $data == NULL means unset().
      $_SESSION[$bin] = $data;
      return;
    default:
      module_invoke_all('session_cache_set', $method, $bin, $data);
  }
}

/**
 * Read data from the user session, given its bin id.
 *
 * @param string $bin
 *   unique id eg a string prefixed by the module name
 */
function session_cache_get($bin) {
  if (!isset($bin)) {
    return NULL;
  }
  $method = variable_get('session_cache_storage_method', SESSION_CACHE_STORAGE_SESSION);
  switch ($method) {
    case SESSION_CACHE_STORAGE_COOKIE:

      // Note that in array indices '.' are converted to '_'
      return isset($_COOKIE["Drupal_session_cache_{$bin}"]) ? json_decode($_COOKIE["Drupal_session_cache_{$bin}"], TRUE) : NULL;
    case SESSION_CACHE_STORAGE_DB_CORE:
      $sid = session_cache_get_sid();
      $cache = cache_get("{$bin}:{$sid}", 'cache_session_cache');
      return is_object($cache) ? $cache->data : NULL;
    case SESSION_CACHE_STORAGE_SESSION:
      return isset($_SESSION) && isset($_SESSION[$bin]) ? $_SESSION[$bin] : NULL;
    default:
      return module_invoke_all('session_cache_get', $method, $bin);
  }
}

/**
 * Implements hook_flush_caches().
 */
function session_cache_flush_caches() {
  if (variable_get('session_cache_storage_method') == SESSION_CACHE_STORAGE_DB_CORE) {
    return array(
      'cache_session_cache',
    );
  }
}

/**
 * Returns an identifier for the current user session.
 *
 * Typically only called when a database or file-based storage mechanism is
 * employd.
 *
 * @return int
 *   session cache id
 */
function session_cache_get_sid() {
  global $user;
  $sid_source = variable_get('session_cache_sid_source', SESSION_CACHE_COOKIE_FOR_SID);
  if ($sid_source == SESSION_CACHE_IP_ADDRESS_FOR_SID) {

    // Do we want '::1' for localhost or dots in file names?
    $sid = ip_address();
    return $sid == '::1' ? 'localhost' : $sid;
  }

  // SESSION_CACHE_UID_FOR_SID falls back on cookie for anonymous users.
  if ($sid_source == SESSION_CACHE_UID_FOR_SID && !empty($user->uid)) {

    // Fine if concurrent sessions for the same auth. user are NOT required.
    return 'user' . $user->uid;
  }
  if (empty($_COOKIE['Drupal_session_cache_sid'])) {

    // Don't use core's session id. Security not a problem, so keep it short.
    $sid = drupal_substr(session_id(), 0, 12);
    global $cookie_domain;

    // .localhost causes problems
    $cdom = $cookie_domain == '.localhost' ? ini_get('session.cookie_domain') : $cookie_domain;

    // If setcookie() fails, then everything still works, but a new session will
    // be created when logging in or out.
    setcookie('Drupal.session_cache.sid', $sid, session_cache_expiration_time(), '/', $cdom, NULL, TRUE);

    // Need to also set $_COOKIE explicitly, see [#2210329].
    $_COOKIE['Drupal_session_cache_sid'] = $sid;
  }
  else {
    $sid = $_COOKIE['Drupal_session_cache_sid'];
  }
  return $sid;
}

/**
 * Returns the date/time that the session cache will expire.
 *
 * @return int
 *   UNIX time stamp
 */
function session_cache_expiration_time() {
  return REQUEST_TIME + 24 * 60 * 60 * variable_get('session_cache_expire_period', SESSION_CACHE_DEFAULT_EXPIRATION_DAYS);
}

/**
 * Implements hook_menu().
 */
function session_cache_menu() {
  $items['admin/config/development/session_cache'] = array(
    'title' => 'Session Cache API',
    'description' => 'Select the session storage mechanism.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'session_cache_admin_config',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'session_cache.admin.inc',
  );
  return $items;
}

Functions

Namesort descending Description
session_cache_expiration_time Returns the date/time that the session cache will expire.
session_cache_flush_caches Implements hook_flush_caches().
session_cache_get Read data from the user session, given its bin id.
session_cache_get_sid Returns an identifier for the current user session.
session_cache_menu Implements hook_menu().
session_cache_set Write data to the user session, whatever the storage mechanism may be.

Constants