Skip to content

Instantly share code, notes, and snippets.

@brtriver
Created November 2, 2010 06:23
Show Gist options
  • Save brtriver/659318 to your computer and use it in GitHub Desktop.
Save brtriver/659318 to your computer and use it in GitHub Desktop.
<?php
/**
* MongoSessionStrage manages session storage via MongoDB
*
* This class stores the session data in via MongoDB and with an id issued in a
* signed cookie. Useful when you don't want to store the session.
*
* @package PHP
* @subpackage session
* @author Masao Maeda <brt.river@gmail.com>
*/
class MongoSessionStorage
{
static protected
$sessionIdRegenerated = false,
$sessionStarted = false,
$db = null,
$col = null,
$options = array(
'db_name' => 'php_session',
'collection_name' => 'session',
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',
'host' => 'localhost',
'port' => '27017',
);
/**
* Available options:
*
* * collection_name: The database table in which session data will be stored
* * db_name: The sfDatabase object to use
* * db_id_col: The database column in which the session id will be stored (sess_id by default)
* * db_data_col: The database column in which the session data will be stored (sess_data by default)
* * db_time_col: The database column in which the session timestamp will be stored (sess_time by default)
*
* @param array $options An associative array of options
*
*/
static public function init($options = array())
{
self::$options = array_merge(self::$options, $options);
if (!isset(self::$options['db_name']))
{
throw new Exception('You must provide a "db_name" option to sfMongoSessionStorage.');
}
if (!isset(self::$options['collection_name']))
{
throw new Exception('You must provide a "collection_name" option to sfMongoSessionStorage.');
}
// use this object as the session handler
session_set_save_handler(array(__CLASS__, 'sessionOpen'),
array(__CLASS__, 'sessionClose'),
array(__CLASS__, 'sessionRead'),
array(__CLASS__, 'sessionWrite'),
array(__CLASS__, 'sessionDestroy'),
array(__CLASS__, 'sessionGC'));
// start our session
//session_start();
}
/**
* Closes a session.
*
* @return boolean true, if the session was closed, otherwise false
*/
static public function sessionClose()
{
// do nothing
return true;
}
/**
* Opens a session.
*
* @param string $path (ignored)
* @param string $name (ignored)
*
* @return boolean true, if the session was opened, otherwise an exception is thrown
*
* @throws <b>Exception</b> If a connection with the database does not exist or cannot be created
*/
static public function sessionOpen($path = null, $name = null)
{
// what host and port are we using?
$host = self::$options['host'];
$port = self::$options['port'];
// what database are we using?
$db_name = self::$options['db_name'];
if (!class_exists('Mongo'))
{
throw new Exception('Mongo class does not exist!');
}
$mongo = new Mongo(sprintf("%s:%s", $host, $port));
self::$db = $mongo->selectDB($db_name);
self::$col = self::$db->selectCollection(self::$options['collection_name']);
if (null === self::$db && null === self::$col)
{
throw new Exception('MongoDB connection does not exist. Unable to open session.');
}
return true;
}
/**
* Destroys a session.
*
* @param string $id A session ID
*
* @return bool true, if the session was destroyed, otherwise an exception is thrown
*
* @throws <b>Exception</b> If the session cannot be destroyed
*/
static public function sessionDestroy($id) {
if (self::$col->remove(array(self::$options['db_id_col'] => $id)))
{
return true;
}
$last_error = self::$db->lastError();
throw new Exception(sprintf('%s cannot destroy session id "%s" (%s).', __CLASS__, $id, $last_error['err']));
}
/**
* Cleans up old sessions.
*
* @param int $lifetime The lifetime of a session
*
* @return bool true, if old sessions have been cleaned, otherwise an exception is thrown
*
* @throws <b>Exception</b> If any old sessions cannot be cleaned
*/
static public function sessionGC($lifetime)
{
// get column
$db_time_col = self::$options['db_time_col'];
// delete the record older than the authorised session life time
if (self::$col->remove(array('$where' => sprintf('this.%s + %d < %d', $db_time_col, $lifetime, time()))))
{
return true;
}
$last_error = self::$db->lastError();
throw new Exception(sprintf('%s cannot delete old sessions (%s).', __CLASS__, $last_error['err']));
}
/**
* Reads a session.
*
* @param string $id A session ID
*
* @return bool true, if the session was read, otherwise an exception is thrown
*
* @throws <b>Exception</b> If the session cannot be read
*/
static public function sessionRead($id)
{
// get column
$db_data_col = self::$options['db_data_col'];
$db_id_col = self::$options['db_id_col'];
$db_time_col = self::$options['db_time_col'];
$obj = self::$col->findOne(array(self::$options['db_id_col'] => $id));
if (count($obj))
{
// found the session
return $obj[$db_data_col];
}
else
{
$obj = array(
$db_id_col => $id,
$db_data_col => '',
$db_time_col => time(),
);
// session does not exist, create it
if (self::$col->insert($obj))
{
//self::$col->ensureIndex(array($db_id_col => 1));
return '';
}
// can't create record
$last_error = self::$db->lastError();
throw new Exception(sprintf('%s cannot create new record for id "%s" (%s).', __CLASS__, $id, $last_error['err']));
}
}
/**
* Writes session data.
*
* @param string $id A session ID
* @param string $data A serialized chunk of session data
*
* @return bool true, if the session was written, otherwise an exception is thrown
*
* @throws <b>Exception</b> If the session data cannot be written
*/
static public function sessionWrite($id, $data)
{
// get column
$db_data_col = self::$options['db_data_col'];
$db_id_col = self::$options['db_id_col'];
$db_time_col = self::$options['db_time_col'];
// update the record associated with this id
$obj = array(
$db_id_col => $id,
$db_data_col => $data,
$db_time_col => time(),
);
if (self::$col->update(array($db_id_col => $id), $obj))
{
return true;
}
// failed to write session data
$last_error = self::$db->lastError();
throw new Exception(sprintf('%s cannot write session data for id "%s" (%s).', __CLASS__, $id, $last_error['err']));
}
/**
* Regenerates id that represents this storage.
*
* @param boolean $destroy Destroy session when regenerating?
*
* @return boolean True if session regenerated, false if error
*
*/
static public function regenerate($destroy = false)
{
if (self::$sessionIdRegenerated)
{
return;
}
$currentId = session_id();
// regenerate a new session id once per object
session_regenerate_id($destroy);
self::$sessionIdRegenerated = true;
$newId = session_id();
self::sessionRead($newId);
return self::sessionWrite($newId, self::sessionRead($currentId));
}
/**
* Executes the shutdown procedure.
*
*/
static public function shutdown()
{
session_write_close();
}
}
<?php
// simple tests
error_reporting(E_ALL);
ini_set('display_errors', 'On');
echo "<h3>start test</h3>";
try{
// (1) include library
require_once dirname(__FILE__) . '/MongoSessionStorage.php';
$options = array(
'db_name' => 'php_session',
'collection_name' => 'session',
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',
'host' => 'localhost',
'port' => '27017',
);
MongoSessionStorage::init($options);
// (2) session start
if (!session_start()) throw new Exception('cannot start session');
// (3) write to session
$hoge = 'red bull!';
$_SESSION['test'] = $hoge;
if ($_SESSION['test'] !== $hoge) throw new Exception("cannot write/read session");
// (4) regenerate session id
// TODO: If I set the first parameter to 'true', I cannot destory the session data from MongoDB...
$id = session_id();
MongoSessionStorage::regenerate();
if ($id === session_id()) throw new Exception("cannot re-generate session id");
// (5) gc, in this teset, MongoDB data are cleaned up.
MongoSessionStorage::sessionGC(-10);
if (MongoSessionStorage::sessionRead(session_id()) !== "") throw new Exception("cannot run gc");
} catch (Exception $e) {
echo "<div style='color:red;'><h4>Failure.</h4>";
echo $e->getMessage();
echo "</div>";
}
echo "<h3>done.</h3>";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment