first commit

This commit is contained in:
renjianbo
2026-01-07 11:40:41 +08:00
parent 2b5d784e31
commit 8f2ed2c108
6466 changed files with 1431506 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
<?php
/**
* Zeed Platform Project
* Based on Zeed Framework & Zend Framework.
*
* BTS - Billing Transaction Service
* CAS - Central Authentication Service
*
* LICENSE
* http://www.zeed.com.cn/license/
*
* @category Zeed
* @package Zeed_ChangeMe
* @subpackage ChangeMe
* @copyright Copyright (c) 2010 Zeed Technologies PRC Inc. (http://www.zeed.com.cn)
* @author Zeed Team (http://blog.zeed.com.cn)
* @since 2010-7-2
* @version SVN: $Id$
*/
abstract class Zeed_Session_Storage_Abstract {
/**
* Session lifetime
*
* @var int
*/
protected $_lifetime = false;
/**
* Whether or not the lifetime of an existing session should be overridden
*
* @var boolean
*/
protected $_overrideLifetime = false;
/**
* Session 保存路径
* 该值从 session_set_save_handler.Open 中传递过来
*
* @var string
*/
protected $_sessionSavePath;
/**
* Session 名称
* 该值从 session_set_save_handler.Open 中传递过来
*
* @var string
*/
protected $_sessionName;
/**
* 设置 Session 生命周期,如果设置了一个无效值那么使用系统配置 PHP.INI
*
* @param integer $lifetime
* @return Zend_Session_SaveHandler_DbTable
*/
public function setLifetime($lifetime)
{
if (empty($lifetime) || $lifetime < 0) {
$this->_lifetime = (int) ini_get('session.gc_maxlifetime');
} else {
$this->_lifetime = (int) $lifetime;
}
}
/**
* 获取 Session 生命周期
*
* @return integer
*/
public function getLifetime()
{
return $this->_lifetime;
}
/**
* 设置 Session 在更新后是否同时更新时间
* 如果设置为否,那么 Session 的生命不会得到延长
*
* @param boolean $overrideLifetime
*/
public function setOverrideLifetime($overrideLifetime)
{
$this->_overrideLifetime = (boolean) $overrideLifetime;
}
/**
* Retrieve whether or not the lifetime of an existing session should be overridden
*
* @return boolean
*/
public function getOverrideLifetime()
{
return $this->_overrideLifetime;
}
/**
* Open Session
*
* @param string $save_path
* @param string $name
* @return boolean
*/
public function open($savePath, $name)
{
$this->_sessionSavePath = $savePath;
$this->_sessionName = $name;
return true;
}
/**
* Close session
*
* @return boolean
*/
public function close()
{
return true;
}
}
// End ^ Native EOL ^ encoding

View File

@@ -0,0 +1,88 @@
<?php
/**
* iNewS Project
*
* LICENSE
*
* http://www.inews.com.cn/license/inews
*
* @category iNewS
* @package ChangeMe
* @subpackage ChangeMe
* @copyright Copyright (c) 2008 Zeed Technologies PRC Inc. (http://www.inews.com.cn)
* @author xSharp ( GTalk: xSharp@gmail.com )
* @since 2010-3-8
* @version SVN: $Id$
*/
/**
* Storage session data to SQL Database Server(MySQL/PostgreSQL/MSSQL/Oracle).
*/
class Zeed_Session_Storage_Db extends Zeed_Db_Model implements Zeed_Session_Storage_Interface
{
/*
* The table name.
*
* @var string
*/
protected $_name = 'session';
public function open($save_path, $name)
{
}
/**
*
* @param Array $set
* @param String $where
* @return Integer
*/
public function close()
{
}
public function read($id)
{
}
/**
*
* @param Array $set
* @return Integer
*/
public function write($id, $data)
{
}
public function destroy($id)
{
}
public function gc($maxlifetime)
{
return true;
}
public function __construct($config)
{
}
public function __destruct()
{
}
/**
* 为啥需要这个函数呢?
* 这个就是用来获取这个Class的类名
*
* @return Zeed_Db_Session
*/
public static function instance()
{
return parent::_instance(__CLASS__);
}
}
// End ^ LF ^ encoding

View File

@@ -0,0 +1,67 @@
<?php
/**
* iNewS Project
*
* LICENSE
*
* http://www.inews.com.cn/license/inews
*
* @category iNewS
* @package ChangeMe
* @subpackage ChangeMe
* @copyright Copyright (c) 2008 Zeed Technologies PRC Inc. (http://www.inews.com.cn)
* @author xSharp ( GTalk: xSharp@gmail.com )
* @since Apr 2, 2010
* @version SVN: $Id$
*/
interface Zeed_Session_Storage_Interface
{
/**
* Open Session - retrieve resources
*
* @param string $save_path
* @param string $name
*/
public function open($save_path, $name);
/**
* Close Session - free resources
*
*/
public function close();
/**
* Read session data
*
* @param string $id
*/
public function read($id);
/**
* Write Session - commit data to resource
*
* @param string $id
* @param mixed $data
*/
public function write($id, $data);
/**
* Destroy Session - remove data from resource for
* given session id
*
* @param string $id
*/
public function destroy($id);
/**
* Garbage Collection - remove old session data older
* than $maxlifetime (in seconds)
*
* @param int $maxlifetime
*/
public function gc($maxlifetime);
}
// End ^ LF ^ encoding

View File

@@ -0,0 +1,31 @@
<?php
/**
* iNewS Project
*
* LICENSE
*
* http://www.inews.com.cn/license/inews
*
* @category iNewS
* @package ChangeMe
* @subpackage ChangeMe
* @copyright Copyright (c) 2008 Zeed Technologies PRC Inc. (http://www.inews.com.cn)
* @author xSharp ( GTalk: xSharp@gmail.com )
* @since Apr 2, 2010
* @version SVN: $Id$
*/
/**
* Zend_Session_SaveHandler_DbTable
*
* @category Zend
* @package Zend_Session
* @subpackage SaveHandler
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zeed_Session_Storage_Libmemcached extends Zeed_Session_Storage_Memcached
{
}
// End ^ LF ^ encoding

View File

@@ -0,0 +1,215 @@
<?php
/**
* iNewS Project
*
* LICENSE
*
* http://www.inews.com.cn/license/inews
*
* @category iNewS
* @package ChangeMe
* @subpackage ChangeMe
* @copyright Copyright (c) 2008 Zeed Technologies PRC Inc. (http://www.inews.com.cn)
* @author xSharp ( GTalk: xSharp@gmail.com )
* @since Apr 2, 2010
* @version SVN: $Id$
*/
/**
* Zend_Session_SaveHandler_DbTable
*
* @category Zend
* @package Zend_Session
* @subpackage SaveHandler
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zeed_Session_Storage_Memcache extends Zend_Cache_Backend_Memcached implements Zeed_Session_Storage_Interface
{
/**
* Session lifetime
*
* @var int
*/
protected $_lifetime = false;
/**
* Whether or not the lifetime of an existing session should be overridden
*
* @var boolean
*/
protected $_overrideLifetime = false;
/**
* Session 保存路径
* 该值从 session_set_save_handler.Open 中传递过来
*
* @var string
*/
protected $_sessionSavePath;
/**
* Session 名称
* 该值从 session_set_save_handler.Open 中传递过来
*
* @var string
*/
protected $_sessionName;
/**
* Constructor
*
* lifetime => (integer) Session lifetime (optional; default: ini_get('session.gc_maxlifetime'))
*
* @param array $config
* @return void
* @throws Zeed_Exception
*/
public function __construct($config)
{
if (! is_array($config)) {
throw new Zeed_Exception('$config must be an array of key/value pairs containing ' . 'configuration options for Zeed_Session_Storage_Memcached.');
}
foreach ($config as $key => $value) {
do {
switch ($key) {
case 'lifetime' :
$this->setLifetime($value);
break;
default :
// unrecognized options passed to parent::__construct()
break 2;
}
unset($config[$key]);
} while (false);
}
parent::__construct($config);
}
/**
* Destructor
*
* @return void
*/
public function __destruct()
{
Zeed_Session::writeClose();
}
/**
* 设置 Session 生命周期,如果设置了一个无效值那么使用系统配置 PHP.INI
*
* @param integer $lifetime
* @return Zend_Session_SaveHandler_DbTable
*/
public function setLifetime($lifetime)
{
if (empty($lifetime) || $lifetime < 0) {
$this->_lifetime = (int) ini_get('session.gc_maxlifetime');
} else {
$this->_lifetime = (int) $lifetime;
}
}
/**
* 获取 Session 生命周期
*
* @return integer
*/
public function getLifetime($specificLifetime = false)
{
return $this->_lifetime;
}
/**
* Open Session
*
* @param string $save_path
* @param string $name
* @return boolean
*/
public function open($savePath, $name)
{
$this->_sessionSavePath = $savePath;
$this->_sessionName = $name;
return true;
}
/**
* Close session
*
* @return boolean
*/
public function close()
{
return true;
}
/**
* Read session data
*
* @param string $id
* @return string
*/
public function read($id)
{
$return = '';
if (false != $data = $this->load($id)) {
$return = $data;
}
return $return;
}
/**
* Write session data
*
* @param string $id
* @param string $data
* @return boolean
*/
public function write($id, $data)
{
$return = false;
if ($this->save($data, $id, array(), $this->_lifetime)) {
$return = true;
}
return $return;
}
/**
* Destroy session
*
* @param string $id
* @return boolean
*/
public function destroy($id)
{
$return = false;
if ($this->remove($id)) {
$return = true;
}
return $return;
}
/**
* Garbage Collection
*
* @param int $maxlifetime
* @return true
*/
public function gc($maxlifetime)
{
return true;
}
}
// End ^ LF ^ encoding

View File

@@ -0,0 +1,215 @@
<?php
/**
* iNewS Project
*
* LICENSE
*
* http://www.inews.com.cn/license/inews
*
* @category iNewS
* @package ChangeMe
* @subpackage ChangeMe
* @copyright Copyright (c) 2008 Zeed Technologies PRC Inc. (http://www.inews.com.cn)
* @author xSharp ( GTalk: xSharp@gmail.com )
* @since Apr 2, 2010
* @version SVN: $Id$
*/
/**
* Zend_Session_SaveHandler_DbTable
*
* @category Zend
* @package Zend_Session
* @subpackage SaveHandler
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zeed_Session_Storage_Memcached extends Zend_Cache_Backend_Libmemcached implements Zeed_Session_Storage_Interface
{
/**
* Session lifetime
*
* @var int
*/
protected $_lifetime = false;
/**
* Whether or not the lifetime of an existing session should be overridden
*
* @var boolean
*/
protected $_overrideLifetime = false;
/**
* Session 保存路径
* 该值从 session_set_save_handler.Open 中传递过来
*
* @var string
*/
protected $_sessionSavePath;
/**
* Session 名称
* 该值从 session_set_save_handler.Open 中传递过来
*
* @var string
*/
protected $_sessionName;
/**
* Constructor
*
* lifetime => (integer) Session lifetime (optional; default: ini_get('session.gc_maxlifetime'))
*
* @param array $config
* @return void
* @throws Zeed_Exception
*/
public function __construct($config)
{
if (! is_array($config)) {
throw new Zeed_Exception('$config must be an array of key/value pairs containing ' . 'configuration options for Zeed_Session_Storage_Memcached.');
}
foreach ($config as $key => $value) {
do {
switch ($key) {
case 'lifetime' :
$this->setLifetime($value);
break;
default :
// unrecognized options passed to parent::__construct()
break 2;
}
unset($config[$key]);
} while (false);
}
parent::__construct($config);
}
/**
* Destructor
*
* @return void
*/
public function __destruct()
{
Zeed_Session::writeClose();
}
/**
* 设置 Session 生命周期,如果设置了一个无效值那么使用系统配置 PHP.INI
*
* @param integer $lifetime
* @return Zend_Session_SaveHandler_DbTable
*/
public function setLifetime($lifetime)
{
if (empty($lifetime) || $lifetime < 0) {
$this->_lifetime = (int) ini_get('session.gc_maxlifetime');
} else {
$this->_lifetime = (int) $lifetime;
}
}
/**
* 获取 Session 生命周期
*
* @return integer
*/
public function getLifetime($specificLifetime = false)
{
return $this->_lifetime;
}
/**
* Open Session
*
* @param string $save_path
* @param string $name
* @return boolean
*/
public function open($savePath, $name)
{
$this->_sessionSavePath = $savePath;
$this->_sessionName = $name;
return true;
}
/**
* Close session
*
* @return boolean
*/
public function close()
{
return true;
}
/**
* Read session data
*
* @param string $id
* @return string
*/
public function read($id)
{
$return = '';
if (false != $data = $this->load($id)) {
$return = $data;
}
return $return;
}
/**
* Write session data
*
* @param string $id
* @param string $data
* @return boolean
*/
public function write($id, $data)
{
$return = false;
if ($this->save($data, $id, array(), $this->_lifetime)) {
$return = true;
}
return $return;
}
/**
* Destroy session
*
* @param string $id
* @return boolean
*/
public function destroy($id)
{
$return = false;
if ($this->remove($id)) {
$return = true;
}
return $return;
}
/**
* Garbage Collection
*
* @param int $maxlifetime
* @return true
*/
public function gc($maxlifetime)
{
return true;
}
}
// End ^ LF ^ encoding

View File

@@ -0,0 +1,279 @@
<?php
/*
* This MongoDB session handler is intended to store any data you see fit.
*/
class Zeed_Session_Storage_Mongo implements Zeed_Session_Storage_Interface
{
/**
* Whether session writes should be performed safely.
* If TRUE, the
* program will wait for a database response and throw a
* MongoCursorException if the update failed. Can also be set to an
* integer value for replication. For more information, see:
* http://www.php.net/manual/en/mongocollection.update.php
* Slower when on but minimizes any session errors when coupled with FSYNC.
*/
const SAFE = false;
/**
* If TRUE, forces the session write to be synced to disk before
* returning success.
*/
const FSYNC = false;
// example config with support for multiple servers
// (helpful for sharding and replication setups)
protected $_config = array(
// cookie related vars
'cookie_path' => '/',
'cookie_domain' => null, // .mydomain.com
// session related vars
'lifetime' => 3600, // session lifetime in seconds
'database' => 'session', // name of MongoDB database
'collection' => 'session', // name of MongoDB collection
// array of mongo db servers
'servers' => array(
array(
'host' => Mongo::DEFAULT_HOST,
'port' => Mongo::DEFAULT_PORT,
'username' => null,
'password' => null,
'persistent' => false)));
// stores the mongo db
protected $mongo;
// stores session data results
private $session;
/**
* Default constructor.
*
* @access public
* @param array $config
*/
public function __construct($config = array())
{
// initialize the database
$this->_init(empty($config) ? $this->_config : $config);
// set object as the save handler
session_set_save_handler(
array(&$this, 'open'),
array(&$this, 'close'),
array(&$this, 'read'),
array(&$this, 'write'),
array(&$this, 'destroy'),
array(&$this, 'gc')
);
// set some important session vars
ini_set('session.auto_start', 0);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', $this->_config['lifetime']);
ini_set('session.referer_check', '');
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.entropy_length', 16);
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.use_trans_sid', 0);
ini_set('session.hash_function', 1);
ini_set('session.hash_bits_per_character', 5);
// disable client/proxy caching
session_cache_limiter('nocache');
// set the cookie parameters
session_set_cookie_params($this->_config['lifetime'], $this->_config['cookie_path'], $this->_config['cookie_domain']);
// name the session
session_name('mongo_sess');
// start it up
session_start();
}
/**
* Initialize MongoDB.
* There is currently no support for persistent
* connections. It would be very easy to implement, I just didn't need it.
*
* @access private
* @param array $config
*/
private function _init($config)
{
// ensure they supplied a database
if (empty($config['database'])) {
throw new Exception('You must specify a MongoDB database to use for session storage.');
}
if (empty($config['collection'])) {
throw new Exception('You must specify a MongoDB collection to use for session storage.');
}
// update config
$this->_config = $config;
// generate server connection strings
$connections = array();
if (! empty($this->_config['servers'])) {
foreach ($this->_config['servers'] as $server) {
$str = '';
if (! empty($server['username']) && ! empty($server['password'])) {
$str .= $server['username'] . ':' . $server['password'] . '@';
}
$str .= $server['host'] . ':' . $server['port'];
array_push($connections, $str);
}
} else {
// use default connection settings
array_push($connections, Mongo::DEFAULT_HOST . ':' . Mongo::DEFAULT_PORT);
}
// load mongo servers
$mongo = new Mongo('mongodb://' . implode(',', $connections));
// load db
try {
$mongo = $mongo->selectDB($this->_config['database']);
} catch (InvalidArgumentException $e) {
throw new Exception('The MongoDB database specified in the config does not exist.');
}
// load collection
try {
$this->mongo = $mongo->selectCollection($this->_config['collection']);
} catch (Exception $e) {
throw new Exception('The MongoDB collection specified in the config does not exist.');
}
// ensure we have proper indexing on the expiration
$this->mongo->ensureIndex('expiry', array('expiry' => 1));
}
/**
* Open does absolutely nothing as we already have an open connection.
*
* @access public
* @return bool
*/
public function open($save_path, $session_name)
{
return true;
}
/**
* Close does absolutely nothing as we can assume __destruct handles
* things just fine.
*
* @access public
* @return bool
*/
public function close()
{
return true;
}
/**
* Read the session data.
*
* @access public
* @param string $id
* @return string
*/
public function read($id)
{
// exclude results that are inactive or expired
$result = $this->mongo->findOne(array('_id' => $id, 'expiry' => array('$gte' => time()), 'active' => 1));
if ($result) {
$this->session = $result;
return $result['data'];
}
$this->mongo->insert(array('_id' => $id, 'data' => null, 'expiry' => 0, 'active' => 0));
return '';
}
/**
* Atomically write data to the session.
*
*
* @access public
* @param string $id
* @param mixed $data
* @return bool
*/
public function write($id, $data)
{
// create expires
$expiry = time() + $this->_config['lifetime'];
// create new session data
$new_obj = array('data' => $data, 'active' => 1, 'expiry' => $expiry);
// check for existing session for merge
if (! empty($this->session)) {
$obj = (array) $this->session;
$new_obj = array_merge($obj, $new_obj);
}
// atomic update
$query = array('_id' => $id);
// update options
$options = array('upsert' => true, 'safe' => Mongo::SAFE, 'fsync' => Mongo::FSYNC);
// perform the update or insert
try {
$this->mongo->update($query, array('$set' => $new_obj), $options);
} catch (Exception $e) {
return false;
}
return true;
}
/**
* Destroys the session by removing the document with
* matching session_id.
*
* @access public
* @param string $id
* @return bool
*/
public function destroy($id)
{
$this->mongo->remove(array('_id' => $id), true);
return true;
}
/**
* Garbage collection.
* Remove all expired entries atomically.
*
* @access public
* @return bool
*/
public function gc()
{
// define the query
$query = array('expiry' => array('$lt' => time()));
// specify the update vars
$update = array('$set' => array('active' => 0));
// update options
$options = array('multiple' => TRUE, 'safe' => Mongo::SAFE, 'fsync' => Mongo::FSYNC);
// update expired elements and set to inactive
$this->mongo->update($query, $update, $options);
return true;
}
}

View File

@@ -0,0 +1,397 @@
<?php
/*
* This MongoDB session handler is intended to store any data you see fit.
* One interesting optimization to note is the setting of the active flag
* to 0 when a session has expired. The intended purpose of this garbage
* collection is to allow you to create a batch process for removal of
* all expired sessions. This should most likely be implemented as a cronjob
* script.
*
* @author Corey Ballou
* @copyright Corey Ballou (2010)
*
*/
class MongoSession {
// default config with support for multiple servers
// (helpful for sharding and replication setups)
protected $_config = array(
// cookie related vars
'cookie_path' => '/',
'cookie_domain' => '.mydomain.com', // .mydomain.com
// session related vars
'lifetime' => 3600, // session lifetime in seconds
'database' => 'session', // name of MongoDB database
'collection' => 'session', // name of MongoDB collection
// persistent related vars
'persistent' => false, // persistent connection to DB?
'persistentId' => 'MongoSession', // name of persistent connection
// whether we're supporting replicaSet
'replicaSet' => false,
// array of mongo db servers
'servers' => array(
array(
'host' => Mongo::DEFAULT_HOST,
'port' => Mongo::DEFAULT_PORT,
'username' => null,
'password' => null
)
)
);
// stores the connection
protected $_connection;
// stores the mongo db
protected $_mongo;
// stores session data results
protected $_session;
/**
* Default constructor.
*
* @access public
* @param array $config
*/
public function __construct($config = array())
{
// initialize the database
$this->_init(empty($config) ? $this->_config : $config);
// set object as the save handler
session_set_save_handler(
array(&$this, 'open'),
array(&$this, 'close'),
array(&$this, 'read'),
array(&$this, 'write'),
array(&$this, 'destroy'),
array(&$this, 'gc')
);
// set some important session vars
ini_set('session.auto_start', 0);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', $this->_config['lifetime']);
ini_set('session.referer_check', '');
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.entropy_length', 16);
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.use_trans_sid', 0);
ini_set('session.hash_function', 1);
ini_set('session.hash_bits_per_character', 5);
// disable client/proxy caching
session_cache_limiter('nocache');
// set the cookie parameters
session_set_cookie_params(
$this->_config['lifetime'],
$this->_config['cookie_path'],
$this->_config['cookie_domain']
);
// name the session
session_name('mongo_sess');
// start it up
session_start();
}
/**
* Initialize MongoDB. There is currently no support for persistent
* connections. It would be very easy to implement, I just didn't need it.
*
* @access private
* @param array $config
*/
private function _init($config)
{
// ensure they supplied a database
if (empty($config['database'])) {
throw new Exception('You must specify a MongoDB database to use for session storage.');
}
if (empty($config['collection'])) {
throw new Exception('You must specify a MongoDB collection to use for session storage.');
}
// update config
$this->_config = $config;
// generate server connection strings
$connections = array();
if (!empty($this->_config['servers'])) {
foreach ($this->_config['servers'] as $server) {
$str = '';
if (!empty($server['username']) && !empty($server['password'])) {
$str .= $server['username'] . ':' . $server['password'] . '@';
}
$str .= !empty($server['host']) ? $server['host'] : Mongo::DEFAULT_HOST;
$str .= ':' . (!empty($server['port']) ? (int) $server['port'] : Mongo::DEFAULT_PORT);
array_push($connections, $str);
}
} else {
// use default connection settings
array_push($connections, Mongo::DEFAULT_HOST . ':' . Mongo::DEFAULT_PORT);
}
// add immediate connection
$opts = array('connect' => true);
// support persistent connections
if ($this->_config['persistent'] && !empty($this->_config['persistentId'])) {
$opts['persist'] = $this->_config['persistentId'];
}
// support replica sets
if ($this->_config['replicaSet']) {
$opts['replicaSet'] = true;
}
// load mongo server connection
try {
$this->_connection = new Mongo('mongodb://' . implode(',', $connections), $opts);
} catch (Exception $e) {
throw new Exception('Can\'t connect to the MongoDB server.');
}
// load the db
try {
$mongo = $this->_connection->selectDB($this->_config['database']);
} catch (InvalidArgumentException $e) {
throw new Exception('The MongoDB database specified in the config does not exist.');
}
// load collection
try {
$this->_mongo = $mongo->selectCollection($this->_config['collection']);
} catch(Exception $e) {
throw new Exception('The MongoDB collection specified in the config does not exist.');
}
// proper indexing on the expiration
$this->_mongo->ensureIndex(
array('expiry' => 1),
array('name' => 'expiry',
'unique' => true,
'dropDups' => true,
'safe' => true
)
);
// proper indexing of session id and lock
$this->_mongo->ensureIndex(
array('session_id' => 1, 'lock' => 1),
array('name' => 'session_id',
'unique' => true,
'dropDups' => true,
'safe' => true
)
);
}
/**
* Open does absolutely nothing as we already have an open connection.
*
* @access public
* @return bool
*/
public function open($save_path, $session_name)
{
return true;
}
/**
* Close does absolutely nothing as we can assume __destruct handles
* things just fine.
*
* @access public
* @return bool
*/
public function close()
{
return true;
}
/**
* Read the session data.
*
* @access public
* @param string $id
* @return string
*/
public function read($id)
{
// obtain a read lock on the data, or subsequently wait for
// the lock to be released
$this->_lock($id);
// exclude results that are inactive or expired
$result = $this->_mongo->findOne(
array(
'session_id' => $id,
'expiry' => array('$gte' => time()),
'active' => 1
)
);
if (isset($result['data'])) {
$this->_session = $result;
return $result['data'];
}
return '';
}
/**
* Atomically write data to the session, ensuring we remove any
* read locks.
*
* @access public
* @param string $id
* @param mixed $data
* @return bool
*/
public function write($id, $data)
{
// create expires
$expiry = time() + $this->_config['lifetime'];
// create new session data
$new_obj = array(
'data' => $data,
'lock' => 0,
'active' => 1,
'expiry' => $expiry
);
// check for existing session for merge
if (!empty($this->_session)) {
$obj = (array) $this->_session;
$new_obj = array_merge($obj, $new_obj);
}
// atomic update
$query = array('session_id' => $id);
// update options
$options = array(
'upsert' => TRUE,
'safe' => TRUE,
'fsync' => TRUE
);
// perform the update or insert
try {
$result = $this->_mongo->update($query, array('$set' => $new_obj), $options);
return $result['ok'] == 1;
} catch (Exception $e) {
return false;
}
return true;
}
/**
* Destroys the session by removing the document with
* matching session_id.
*
* @access public
* @param string $id
* @return bool
*/
public function destroy($id)
{
$this->_mongo->remove(array('session_id' => $id), true);
return true;
}
/**
* Garbage collection. Remove all expired entries atomically.
*
* @access public
* @return bool
*/
public function gc()
{
// define the query
$query = array('expiry' => array('$lt' => time()));
// specify the update vars
$update = array('$set' => array('active' => 0));
// update options
$options = array(
'multiple' => TRUE,
'safe' => TRUE,
'fsync' => TRUE
);
// update expired elements and set to inactive
$this->_mongo->update($query, $update, $options);
return true;
}
/**
* Solves issues with write() and close() throwing exceptions.
*
* @access public
* @return void
*/
public function __destruct()
{
session_write_close();
}
/**
* Create a global lock for the specified document.
*
* @author Benson Wong (mostlygeek@gmail.com)
* @access private
* @param string $id
*/
private function _lock($id)
{
$remaining = 30000000;
$timeout = 5000;
do {
try {
$query = array('session_id' => $id, 'lock' => 0);
$update = array('$set' => array('lock' => 1));
$options = array('safe' => true, 'upsert' => true);
$result = $this->_mongo->update($query, $update, $options);
if ($result['ok'] == 1) {
return true;
}
} catch (MongoCursorException $e) {
if (substr($e->getMessage(), 0, 26) != 'E11000 duplicate key error') {
throw $e; // not a dup key?
}
}
// force delay in microseconds
usleep($timeout);
$remaining -= $timeout;
// backoff on timeout, save a tree. max wait 1 second
$timeout = ($timeout < 1000000) ? $timeout * 2 : 1000000;
} while ($remaining > 0);
// aww shit.
throw new Exception('Could not obtain a session lock.');
}
}