}
$params = Horde::getDriverConfig('sessionhandler', $driver);
- if (strcasecmp($driver, 'Sql') === 0) {
- $params['db'] = $injector->getInstance('Horde_Db_Adapter');
- } elseif (strcasecmp($driver, 'Memcache') === 0) {
- $params['memcache'] = $injector->getInstance('Horde_Memcache');
- } elseif (strcasecmp($driver, 'Ldap') === 0) {
+ $driver = basename(Horde_String::lower($driver));
+ $noset = false;
+
+ switch ($driver) {
+ case 'builtin':
+ $noset = true;
+ break;
+
+ case 'ldap':
$params['ldap'] = $injector->getInstances('Horde_Core_Factory_Ldap')->getLdap('horde', 'sessionhandler');
+ break;
+
+ case 'memcache':
+ $params['memcache'] = $injector->getInstance('Horde_Memcache');
+ break;
+
+ case 'sql':
+ $params['db'] = $injector->getInstance('Horde_Db_Adapter');
+ break;
}
- $logger = $injector->getInstance('Horde_Log_Logger');
+ $class = 'Horde_SessionHandler_Storage_' . Horde_String::ucfirst($driver);
+ if (!class_exists($class)) {
+ throw new Horde_SessionHandler_Exception('Driver not found: ' . $class);
+ }
+ $storage = new $class($params);
if (!empty($conf['sessionhandler']['memcache']) &&
- (strcasecmp($driver, 'Builtin') != 0) &&
- (strcasecmp($driver, 'Memcache') != 0)) {
- $params = array(
+ !in_array($driver, array('builtin', 'memcache'))) {
+ $storage = new Horde_SessionHandler_Storage_Stack(array(
'stack' => array(
- array(
- 'driver' => 'Memcache',
- 'params' => array(
- 'memcache' => $injector->getInstance('Horde_Memcache'),
- 'logger' => $logger
- )
- ),
- array(
- 'driver' => $driver,
- 'params' => array_merge($params, array(
- 'logger' => $logger
- ))
- )
+ new Horde_SessionHandler_Storage_Memcache(array(
+ 'memcache' => $injector->getInstance('Horde_Memcache')
+ )),
+ $storage
)
- );
- $driver = 'Stack';
+ ));
}
- $params['logger'] = $logger;
- // TODO: Uncomment once all session data is saved through
- // Horde_Session.
- //$params['no_md5'] = true
- $params['parse'] = array($this, 'readSessionData');
-
- $driver = basename(strtolower($driver));
- $class = 'Horde_SessionHandler_' . ucfirst($driver);
-
- if (class_exists($class)) {
- return new $class($params);
- }
- throw new Horde_SessionHandler_Exception('Driver not found: ' . $driver);
+ return new Horde_SessionHandler(
+ $storage,
+ array(
+ 'logger' => $injector->getInstance('Horde_Log_Logger'),
+ // TODO: Uncomment once all session data is saved through
+ // Horde_Session.
+ //'no_md5' => true,
+ 'noset' => $noset,
+ 'parse' => array($this, 'readSessionData')
+ )
+ );
}
/**
<?php
/**
- * This is the abstract class that all drivers inherit from.
+ * This class provides the interface to the session storage backend.
*
* Copyright 2002-2010 The Horde Project (http://www.horde.org/)
*
* @license http://www.fsf.org/copyleft/lgpl.html LGPL
* @package SessionHandler
*/
-abstract class Horde_SessionHandler
+class Horde_SessionHandler
{
/**
* If true, indicates the session data has changed.
protected $_logger;
/**
- * Hash containing connection parameters.
+ * Configuration parameters.
*
* @var array
*/
protected $_sig;
/**
+ * The storage object.
+ *
+ * @var Horde_SessionHandler_Storage
+ */
+ protected $_storage;
+
+ /**
* Constructor.
*
- * @param array $params Parameters:
+ * @param Horde_SessionHandler_Storage $storage The storage object.
+ * @param array $params Configuration parameters:
* <pre>
* logger - (Horde_Log_Logger) A logger instance.
* DEFAULT: No logging
* DEFAULT: No
* </pre>
*/
- public function __construct(array $params = array())
+ public function __construct(Horde_SessionHandler_Storage $storage,
+ array $params = array())
{
$params = array_merge($this->_params, $params);
if (isset($params['logger'])) {
$this->_logger = $params['logger'];
unset($params['logger']);
+
+ $storage->setLogger($this->_logger);
}
$this->_params = $params;
+ $this->_storage = $storage;
if (empty($this->_params['noset'])) {
ini_set('session.save_handler', 'user');
{
if (!$this->_connected) {
try {
- $this->_open($save_path, $session_name);
+ $this->_storage->open($save_path, $session_name);
} catch (Horde_SessionHandler_Exception $e) {
if ($this->_logger) {
$this->_logger->log($e, 'ERR');
}
/**
- * Open the backend.
- *
- * @param string $save_path The path to the session object.
- * @param string $session_name The name of the session.
- *
- * @throws Horde_SessionHandler_Exception
- */
- abstract protected function _open($save_path = null, $session_name = null);
-
- /**
* Close the backend.
*
* @return boolean True on success, false otherwise.
public function close()
{
try {
- $this->_close();
+ $this->_storage->close();
} catch (Horde_SessionHandler_Exception $e) {
if ($this->_logger) {
$this->_logger->log($e, 'ERR');
}
/**
- * Close the backend.
- *
- * @throws Horde_SessionHandler_Exception
- */
- abstract protected function _close();
-
- /**
* Read the data for a particular session identifier from the backend.
* This method should only be called internally by PHP via
* session_set_save_handler().
*/
public function read($id)
{
- $result = $this->_read($id);
+ $result = $this->_storage->read($id);
if (empty($this->_params['no_md5'])) {
$this->_sig = md5($result);
}
}
/**
- * Read the data for a particular session identifier from the backend.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- abstract protected function _read($id);
-
- /**
* Write session data to the backend.
* This method should only be called internally by PHP via
* session_set_save_handler().
if ($this->changed ||
(empty($this->_params['no_md5']) &&
($this->_sig != md5($session_data)))) {
- return $this->_write($id, $session_data);
+ return $this->_storage->write($id, $session_data);
}
if ($this->_logger) {
}
/**
- * Write session data to the backend.
- *
- * @param string $id The session identifier.
- * @param string $session_data The session data.
- *
- * @return boolean True on success, false otherwise.
- */
- abstract protected function _write($id, $session_data);
-
- /**
* Destroy the data for a particular session identifier in the backend.
* This method should only be called internally by PHP via
* session_set_save_handler().
*
* @return boolean True on success, false otherwise.
*/
- abstract public function destroy($id);
+ public function destroy($id)
+ {
+ return $this->_storage->destroy($id);
+ }
/**
* Garbage collect stale sessions from the backend.
*
* @return boolean True on success, false otherwise.
*/
- abstract public function gc($maxlifetime = 300);
-
- /**
- * Get session data read-only.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _readOnly($id)
+ public function gc($maxlifetime = 300)
{
- return $this->read($id);
+ return $this->_storage->gc($id);
}
/**
* @return array A list of valid session identifiers.
* @throws Horde_SessionHandler_Exception
*/
- abstract public function getSessionIDs();
+ public function getSessionIDs()
+ {
+ return $this->_storage->getSessionIDs();
+ }
/**
* Returns a list of authenticated users and data about their session.
$sessions = $this->getSessionIDs();
+ $this->_storage->readonly = true;
+
foreach ($sessions as $id) {
try {
- $data = $this->_readOnly($id);
+ $data = $this->read($id);
} catch (Horde_SessionHandler_Exception $e) {
continue;
}
}
}
+ $this->_storage->readonly = false;
+
return $info;
}
+++ /dev/null
-<?php
-/**
- * Horde_SessionHandler implementation for PHP's built-in session handler.
- * This doesn't do any session handling itself - instead, it exists to allow
- * utility features to be used with the built-in PHP handler.
- *
- * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Matt Selsky <selsky@columbia.edu>
- * @category Horde
- * @package SessionHandler
- */
-class Horde_SessionHandler_Builtin extends Horde_SessionHandler
-{
- /**
- * Directory with session files.
- *
- * @var string
- */
- protected $_path;
-
- /**
- * Constructor.
- *
- * @param array $params Parameters.
- */
- public function __construct(array $params = array())
- {
- parent::__construct(array_merge($params, array(
- 'noset' => true
- )));
-
- $this->_path = session_save_path();
- if (!$this->_path) {
- $this->_path = Horde_Util::getTempDir();
- }
- }
-
- /**
- * Open the backend.
- *
- * @param string $save_path The path to the session object.
- * @param string $session_name The name of the session.
- */
- protected function _open($save_path = null, $session_name = null)
- {
- }
-
- /**
- * Close the backend.
- *
- * @throws Horde_Exception
- */
- protected function _close()
- {
- }
-
- /**
- * Read the data for a particular session identifier from the backend.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _read($id)
- {
- $file = $this->_path . '/sess_' . $id;
- $session_data = @file_get_contents($file);
- if (($session_data === false) && $this->_logger) {
- $this->_logger->log('Unable to read file: ' . $file, 'ERR');
- }
-
- return strval($session_data);
- }
-
- /**
- * Write session data to the backend.
- *
- * @param string $id The session identifier.
- * @param string $session_data The session data.
- *
- * @return boolean True on success, false otherwise.
- */
- protected function _write($id, $session_data)
- {
- return false;
- }
-
- /**
- * Destroy the data for a particular session identifier in the backend.
- * This method should only be called internally by PHP via
- * session_set_save_handler().
- *
- * @param string $id The session identifier.
- *
- * @return boolean True on success, false otherwise.
- */
- public function destroy($id)
- {
- return false;
- }
-
- /**
- * Garbage collect stale sessions from the backend.
- * This method should only be called internally by PHP via
- * session_set_save_handler().
- *
- * @param integer $maxlifetime The maximum age of a session.
- *
- * @return boolean True on success, false otherwise.
- */
- public function gc($maxlifetime = 300)
- {
- return false;
- }
-
- /**
- * Get a list of the valid session identifiers.
- *
- * @return array A list of valid session identifiers.
- */
- public function getSessionIDs()
- {
- $sessions = array();
-
- try {
- $di = new DirectoryIterator($this->_path);
- } catch (UnexpectedValueException $e) {
- return $sessions;
- }
-
- foreach ($di as $val) {
- /* Make sure we're dealing with files that start with sess_. */
- if ($val->isFile() &&
- (strpos($val->getFilename(), 'sess_') === 0)) {
- $sessions[] = substr($val->getFilename(), strlen('sess_'));
- }
- }
-
- return $sessions;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Horde_SessionHandler_External implements an external save handler defined
- * via driver configuration parameters.
- *
- * Copyright 2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @package SessionHandler
- */
-class Horde_SessionHandler_External extends Horde_SessionHandler
-{
- /**
- * Constructor.
- *
- * @param array $params Required parameters:
- * <pre>
- * 'close' - (callback) See session_set_save_handler().
- * 'destroy' - (callback) See session_set_save_handler().
- * 'gc' - (callback) See session_set_save_handler().
- * 'open' - (callback) See session_set_save_handler().
- * 'read' - (callback) See session_set_save_handler().
- * 'write' - (callback) See session_set_save_handler().
- * </pre>
- *
- * @throws InvalidArgumentException
- */
- public function __construct(array $params = array())
- {
- foreach (array('open', 'close', 'read', 'write', 'destroy', 'gc') as $val) {
- if (!isset($params[$val])) {
- throw new InvalidArgumentException('Missing parameter: ' . $val);
- }
- }
-
- parent::__construct($params);
- }
-
- /**
- * Open the backend.
- *
- * @param string $save_path The path to the session object.
- * @param string $session_name The name of the session.
- */
- protected function _open($save_path = null, $session_name = null)
- {
- call_user_func($this->_params['open'], $save_path, $session_name);
- }
-
- /**
- * Close the backend.
- */
- protected function _close()
- {
- call_user_func($this->_params['close']);
- }
-
- /**
- * Read the data for a particular session identifier from the backend.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _read($id)
- {
- return call_user_func($this->_params['read']);
- }
-
- /**
- * Write session data to the backend.
- *
- * @param string $id The session identifier.
- * @param string $session_data The session data.
- *
- * @return boolean True on success, false otherwise.
- */
- protected function _write($id, $session_data)
- {
- return call_user_func($this->_params['write'], $id, $session_data);
- }
-
- /**
- * Destroy the data for a particular session identifier in the backend.
- * This method should only be called internally by PHP via
- * session_set_save_handler().
- *
- * @param string $id The session identifier.
- *
- * @return boolean True on success, false otherwise.
- */
- public function destroy($id)
- {
- return call_user_func($this->_params['destroy'], $id);
- }
-
- /**
- * Garbage collect stale sessions from the backend.
- * This method should only be called internally by PHP via
- * session_set_save_handler().
- *
- * @param integer $maxlifetime The maximum age of a session.
- *
- * @return boolean True on success, false otherwise.
- */
- public function gc($maxlifetime = 300)
- {
- return call_user_func($this->_params['gc'], $maxlifetime);
- }
-
- /**
- * Get a list of the valid session identifiers.
- *
- * @throws Horde_SessionHandler_Exception
- */
- public function getSessionIDs()
- {
- throw new Horde_SessionHandler_Exception('Driver does not support listing session IDs.');
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Horde_SessionHandler implementation for memcache.
- *
- * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Rong-En Fan <rafan@infor.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @package SessionHandler
- */
-class Horde_SessionHandler_Memcache extends Horde_SessionHandler
-{
- /**
- * Memcache object.
- *
- * @var Horde_Memcache
- */
- protected $_memcache;
-
- /**
- * Current session ID.
- *
- * @var string
- */
- protected $_id;
-
- /**
- * Do read-only get?
- *
- * @var boolean
- */
- protected $_readonly = false;
-
- /**
- * The ID used for session tracking.
- *
- * @var string
- */
- protected $_trackID = 'horde_memcache_sessions_track';
-
- /**
- * Constructor.
- *
- * @param array $params Parameters:
- * <pre>
- * 'memcache' - (Horde_Memcache) [REQUIRED] A memcache object.
- * 'track' - (boolean) Track active sessions?
- * 'track_lifetime' - (integer) The number of seconds after which tracked
- * sessions will be treated as expired.
- * </pre>
- *
- * @throws InvalidArgumentException
- */
- public function __construct(array $params = array())
- {
- if (empty($params['memcache'])) {
- throw new InvalidArgumentException('Missing memcache argument.');
- }
-
- $this->_memcache = $params['memcache'];
- unset($params['memcache']);
-
- parent::__construct($params);
-
- if (empty($this->_params['track_lt'])) {
- $this->_params['track_lt'] = ini_get('session.gc_maxlifetime');
- }
-
- if (!empty($this->_params['track']) && (rand(0, 999) == 0)) {
- register_shutdown_function(array($this, 'trackGC'));
- }
- }
-
- /**
- * Open the backend.
- *
- * @param string $save_path The path to the session object.
- * @param string $session_name The name of the session.
- *
- * @throws Horde_SessionHandler_Exception
- */
- protected function _open($save_path = null, $session_name = null)
- {
- }
-
- /**
- * Close the backend.
- */
- protected function _close()
- {
- if (isset($this->_id)) {
- $this->_memcache->unlock($this->_id);
- }
- }
-
- /**
- * Read the data for a particular session identifier.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _read($id)
- {
- if (!$this->_readonly) {
- $this->_memcache->lock($id);
- }
- $result = $this->_memcache->get($id);
-
- if ($result === false) {
- if (!$this->_readonly) {
- $this->_memcache->unlock($id);
- }
-
- if ($result === false) {
- if ($this->_logger) {
- $this->_logger->log('Error retrieving session data (id = ' . $id . ')', 'DEBUG');
- }
- return false;
- }
- }
-
- if (!$this->_readonly) {
- $this->_id = $id;
- }
-
- if ($this->_logger) {
- $this->_logger->log('Read session data (id = ' . $id . ')', 'DEBUG');
- }
-
- return $result;
- }
-
- /**
- * Write session data to the backend.
- *
- * @param string $id The session identifier.
- * @param string $session_data The session data.
- *
- * @return boolean True on success, false otherwise.
- */
- protected function _write($id, $session_data)
- {
- if (!empty($this->_params['track'])) {
- // Do a replace - the only time it should fail is if we are
- // writing a session for the first time. If that is the case,
- // update the session tracker.
- $res = $this->_memcache->replace($id, $session_data);
- $track = !$res;
- } else {
- $res = $track = false;
- }
-
- if (!$res &&
- !$this->_memcache->set($id, $session_data)) {
- if ($this->_logger) {
- $this->_logger->log('Error writing session data (id = ' . $id . ')', 'ERR');
- }
- return false;
- }
-
- if ($track) {
- $this->_memcache->lock($this->_trackID);
- $ids = $this->_memcache->get($this->_trackID);
- if ($ids === false) {
- $ids = array();
- }
-
- $ids[$id] = time();
- $this->_memcache->set($this->_trackID, $ids);
- $this->_memcache->unlock($this->_trackID);
- }
-
- if ($this->_logger) {
- $this->_logger->log('Wrote session data (id = ' . $id . ')', 'DEBUG');
- }
-
- return true;
- }
-
- /**
- * Destroy the data for a particular session identifier.
- *
- * @param string $id The session identifier.
- *
- * @return boolean True on success, false otherwise.
- */
- public function destroy($id)
- {
- $result = $this->_memcache->delete($id);
- $this->_memcache->unlock($id);
-
- if ($result === false) {
- if ($this->_logger) {
- $this->_logger->log('Failed to delete session (id = ' . $id . ')', 'DEBUG');
- }
- return false;
- }
-
- if (!empty($this->_params['track'])) {
- $this->_memcache->lock($this->_trackID);
- $ids = $this->_memcache->get($this->_trackID);
- if ($ids !== false) {
- unset($ids[$id]);
- $this->_memcache->set($this->_trackID, $ids);
- }
- $this->_memcache->unlock($this->_trackID);
- }
-
- if ($this->_logger) {
- $this->_logger->log('Deleted session data (id = ' . $id . ')', 'DEBUG');
- }
-
- return true;
- }
-
- /**
- * Garbage collect stale sessions from the backend.
- *
- * @param integer $maxlifetime The maximum age of a session.
- *
- * @return boolean True on success, false otherwise.
- */
- public function gc($maxlifetime = 300)
- {
- // Memcache does its own garbage collection.
- return true;
- }
-
- /**
- * Get a list of (possibly) valid session identifiers.
- *
- * @return array A list of session identifiers.
- * @throws Horde_SessionHandler_Exception
- */
- public function getSessionIDs()
- {
- if (empty($this->_params['track'])) {
- throw new Horde_SessionHandler_Exception('Memcache session tracking not enabled.');
- }
-
- $this->trackGC();
-
- $ids = $this->_memcache->get($this->_trackID);
-
- return ($ids === false)
- ? array()
- : array_keys($ids);
- }
-
- /**
- * Get session data read-only.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _readOnly($id)
- {
- $this->_readonly = true;
- $result = $this->_memcache->get($id);
- $this->_readonly = false;
-
- return $result;
- }
-
- /**
- * Do garbage collection for session tracking information.
- */
- public function trackGC()
- {
- $this->_memcache->lock($this->_trackID);
- $ids = $this->_memcache->get($this->_trackID);
- if (empty($ids)) {
- $this->_memcache->unlock($this->_trackID);
- return;
- }
-
- $tstamp = time() - $this->_params['track_lt'];
- $alter = false;
-
- foreach ($ids as $key => $val) {
- if ($tstamp > $val) {
- unset($ids[$key]);
- $alter = true;
- }
- }
-
- if ($alter) {
- $this->_memcache->set($this->_trackID, $ids);
- }
-
- $this->_memcache->unlock($this->_trackID);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Horde_SessionHandler implementation for SQL databases.
- *
- * The table structure can be found in:
- * horde/scripts/sql/horde_sessionhandler.sql.
- *
- * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Mike Cochrane <mike@graftonhall.co.nz>
- * @category Horde
- * @package SessionHandler
- */
-class Horde_SessionHandler_Sql extends Horde_SessionHandler
-{
- /**
- * Handle for the current database connection.
- *
- * @var Horde_Db_Adapter
- */
- protected $_db;
-
- /**
- * Constructor.
- *
- * @param array $params Parameters:
- * <pre>
- * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
- * 'table' - (string) The name of the sessions table.
- * DEFAULT: 'horde_sessionhandler'
- * </pre>
- *
- * @throws InvalidArgumentException
- */
- public function __construct(array $params = array())
- {
- if (!isset($params['db'])) {
- throw new InvalidArgumentException('Missing db parameter.');
- }
- $this->_db = $params['db'];
- unset($params['db']);
-
- parent::__construct(array_merge(array(
- 'table' => 'horde_sessionhandler'
- ), $params));
- }
-
- /**
- * Close the backend.
- *
- * @throws Horde_SessionHandler_Exception
- */
- protected function _close()
- {
- /* Close any open transactions. */
- try {
- $this->_db->commitDbTransaction();
- } catch (Horde_Db_Exception $e) {
- throw new Horde_SessionHandler_Exception($e);
- }
- }
-
- /**
- * Read the data for a particular session identifier from the backend.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _read($id)
- {
- /* Begin a transaction. */
- // TODO: Rowlocking in Mysql
- $this->_db->beginDbTransaction();
-
- /* Build query. */
- $query = sprintf('SELECT session_data FROM %s WHERE session_id = ?',
- $this->_params['table']);
- $values = array($id);
-
- /* Execute the query. */
- try {
- return $this->_db->selectValue($query, $values);
- } catch (Horde_Db_Exception $e) {
- return false;
- }
- }
-
- /**
- * Write session data to the backend.
- *
- * @param string $id The session identifier.
- * @param string $session_data The session data.
- *
- * @return boolean True on success, false otherwise.
- */
- protected function _write($id, $session_data)
- {
- /* Build the SQL query. */
- $query = sprintf('SELECT session_id FROM %s WHERE session_id = ?',
- $this->_params['table']);
- $values = array($id);
-
- /* Execute the query. */
- try {
- $result = $this->_db->selectValue($query, $values);
- } catch (Horde_Db_Exception $e) {
- return false;
- }
-
- /* Build the replace SQL query. */
- $query = sprintf('REPLACE INTO %s ' .
- '(session_id, session_data, session_lastmodified) ' .
- 'VALUES (?, ?, ?)',
- $this->_params['table']);
- $values = array(
- $id,
- $session_data,
- time()
- );
-
- /* Execute the replace query. */
- try {
- $this->_db->update($query, $values);
- $this->_db->commitDbTransaction();
- } catch (Horde_Db_Exception $e) {
- $this->_db->rollbackDbTransaction();
- return false;
- }
-
- return true;
- }
-
- /**
- * Destroy the data for a particular session identifier in the backend.
- *
- * @param string $id The session identifier.
- *
- * @return boolean True on success, false otherwise.
- */
- public function destroy($id)
- {
- /* Build the SQL query. */
- $query = sprintf('DELETE FROM %s WHERE session_id = ?',
- $this->_params['table']);
- $values = array($id);
-
- /* Execute the query. */
- try {
- $this->_db->delete($query, $values);
- $this->_db->commitDbTransaction();
- } catch (Horde_Db_Exception $e) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Garbage collect stale sessions from the backend.
- *
- * @param integer $maxlifetime The maximum age of a session.
- *
- * @return boolean True on success, false otherwise.
- */
- public function gc($maxlifetime = 300)
- {
- /* Build the SQL query. */
- $query = sprintf('DELETE FROM %s WHERE session_lastmodified < ?',
- $this->_params['table']);
- $values = array(time() - $maxlifetime);
-
- /* Execute the query. */
- try {
- $this->_db->delete($query, $values);
- } catch (Horde_Db_Exception $e) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Get a list of the valid session identifiers.
- *
- * @return array A list of valid session identifiers.
- */
- public function getSessionIDs()
- {
- $this->open();
-
- /* Build the SQL query. */
- $query = sprintf('SELECT session_id FROM %s' .
- ' WHERE session_lastmodified >= ?',
- $this->_params['table']);
- $values = array(time() - ini_get('session.gc_maxlifetime'));
-
- /* Execute the query. */
- try {
- return $this->_db->selectValues($query, $values);
- } catch (Horde_Db_Exception $e) {
- return array();
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Horde_SessionHandler_Stack is an implementation that will loop through a
- * given list of Horde_SessionHandler drivers to return the session
- * information. This driver allows for use of caching backends on top of
- * persistent backends.
- *
- * Copyright 2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @package SessionHandler
- */
-class Horde_SessionHandler_Stack extends Horde_SessionHandler
-{
- /**
- * Stack of sessionhandlers.
- *
- * @var string
- */
- protected $_stack = array();
-
- /**
- * Constructor.
- *
- * @param array $params Parameters:
- * <pre>
- * 'stack' - (array) [REQUIRED] A list of sessionhandlers to loop
- * through, in order of priority. The last entry is considered
- * the "master" server.
- * Each value should contain an array with two keys: 'driver', a
- * string value with the SessionHandler driver to use, and
- * 'params', containing any parameters needed by this driver.
- * </pre>
- *
- * @throws InvalidArgumentException
- */
- public function __construct(array $params = array())
- {
- if (!isset($params['stack'])) {
- throw new InvalidArgumentException('Missing stack parameter.');
- }
-
- foreach ($params['stack'] as $val) {
- $this->_stack[] = Horde_SessionHandler::factory($val['driver'], $val['params']);
- }
-
- unset($params['stack']);
-
- parent::__construct($params);
- }
-
- /**
- * Open the backend.
- *
- * @param string $save_path The path to the session object.
- * @param string $session_name The name of the session.
- *
- * @throws Horde_SessionHandler_Exception
- */
- protected function _open($save_path = null, $session_name = null)
- {
- foreach ($this->_stack as $val) {
- $val->open($save_path, $session_name);
- }
- }
-
- /**
- * Close the backend.
- *
- * @throws Horde_SessionHandler_Exception
- */
- protected function _close()
- {
- foreach ($this->_stack as $val) {
- $val->close();
- }
- }
-
- /**
- * Read the data for a particular session identifier from the backend.
- *
- * @param string $id The session identifier.
- *
- * @return string The session data.
- */
- protected function _read($id)
- {
- foreach ($this->_stack as $val) {
- $result = $val->read($id);
- if ($result === false) {
- break;
- }
- }
-
- return $result;
- }
-
- /**
- * Write session data to the backend.
- *
- * @param string $id The session identifier.
- * @param string $session_data The session data.
- *
- * @return boolean True on success, false otherwise.
- */
- protected function _write($id, $session_data)
- {
- /* Do writes in *reverse* order - it is OK if a write to one of the
- * non-master backends fails. */
- $master = true;
-
- foreach (array_reverse($this->_stack) as $val) {
- $result = $val->write($id, $session_data);
- if ($result === false) {
- if ($master) {
- return false;
- }
- /* Attempt to invalidate cache if write failed. */
- $val->destroy($id);
- }
- $master = false;
- }
-
- return true;
- }
-
- /**
- * Destroy the data for a particular session identifier in the backend.
- * This method should only be called internally by PHP via
- * session_set_save_handler().
- *
- * @param string $id The session identifier.
- *
- * @return boolean True on success, false otherwise.
- */
- public function destroy($id)
- {
- /* Only report success from master. */
- $master = $success = true;
-
- foreach (array_reverse($this->_stack) as $val) {
- $result = $val->destroy($id);
- if ($master && ($result === false)) {
- $success = false;
- }
- $master = false;
- }
-
- return $success;
- }
-
- /**
- * Garbage collect stale sessions from the backend.
- * This method should only be called internally by PHP via
- * session_set_save_handler().
- *
- * @param integer $maxlifetime The maximum age of a session.
- *
- * @return boolean True on success, false otherwise.
- */
- public function gc($maxlifetime = 300)
- {
- /* Only report GC results from master. */
- foreach ($this->_stack as $val) {
- $result = $val->gc($maxlifetime);
- }
-
- return $result;
- }
-
- /**
- * Get a list of the valid session identifiers.
- *
- * @return array A list of valid session identifiers.
- * @throws Horde_SessionHandler_Exception
- */
- public function getSessionIDs()
- {
- /* Grab session ID list from the master. */
- $ob = end($this->_stack);
- return $ob->getSessionIDs();
- }
-
-}
--- /dev/null
+<?php
+/**
+ * This is the abstract class that all storage drivers inherit from.
+ *
+ * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package SessionHandler
+ */
+abstract class Horde_SessionHandler_Storage
+{
+ /**
+ * Access session read-only?
+ *
+ * @var boolean
+ */
+ public $readonly = false;
+
+ /**
+ * A logger instance.
+ *
+ * @var Horde_Log_Logger
+ */
+ protected $_logger;
+
+ /**
+ * Hash containing connection parameters.
+ *
+ * @var array
+ */
+ protected $_params = array();
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Configuration parameters.
+ */
+ public function __construct(array $params = array())
+ {
+ $params = array_merge($this->_params, $params);
+ }
+
+ /**
+ * Set the logger object.
+ *
+ * @param Horde_Log_Logger $log The logger instance.
+ */
+ public function setLogger(Horde_Log_Logger $log)
+ {
+ $this->_logger = $log;
+ }
+
+ /**
+ * Open the backend.
+ *
+ * @param string $save_path The path to the session object.
+ * @param string $session_name The name of the session.
+ *
+ * @throws Horde_SessionHandler_Exception
+ */
+ abstract public function open($save_path = null, $session_name = null);
+
+ /**
+ * Close the backend.
+ *
+ * @throws Horde_SessionHandler_Exception
+ */
+ abstract public function close();
+
+ /**
+ * Read the data for a particular session identifier from the backend.
+ *
+ * @param string $id The session identifier.
+ *
+ * @return string The session data.
+ */
+ abstract public function read($id);
+
+ /**
+ * Write session data to the backend.
+ *
+ * @param string $id The session identifier.
+ * @param string $session_data The session data.
+ *
+ * @return boolean True on success, false otherwise.
+ */
+ abstract public function write($id, $session_data);
+
+ /**
+ * Destroy the data for a particular session identifier in the backend.
+ * This method should only be called internally by PHP via
+ * session_set_save_handler().
+ *
+ * @param string $id The session identifier.
+ *
+ * @return boolean True on success, false otherwise.
+ */
+ abstract public function destroy($id);
+
+ /**
+ * Garbage collect stale sessions from the backend.
+ * This method should only be called internally by PHP via
+ * session_set_save_handler().
+ *
+ * @param integer $maxlifetime The maximum age of a session.
+ *
+ * @return boolean True on success, false otherwise.
+ */
+ abstract public function gc($maxlifetime = 300);
+
+ /**
+ * Get a list of the valid session identifiers.
+ *
+ * @return array A list of valid session identifiers.
+ * @throws Horde_SessionHandler_Exception
+ */
+ abstract public function getSessionIDs();
+
+}
--- /dev/null
+<?php
+/**
+ * SessionHandler storage implementation for PHP's built-in session handler.
+ * This doesn't do any session handling itself - instead, it exists to allow
+ * utility features to be used with the built-in PHP handler.
+ *
+ * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Matt Selsky <selsky@columbia.edu>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package SessionHandler
+ */
+class Horde_SessionHandler_Storage_Builtin extends Horde_SessionHandler_Storage
+{
+ /**
+ * Directory with session files.
+ *
+ * @var string
+ */
+ protected $_path;
+
+ /**
+ */
+ public function __construct(array $params = array())
+ {
+ parent::__construct($params);
+
+ $this->_path = session_save_path();
+ if (!$this->_path) {
+ $this->_path = Horde_Util::getTempDir();
+ }
+ }
+
+ /**
+ */
+ public function open($save_path = null, $session_name = null)
+ {
+ }
+
+ /**
+ */
+ public function close()
+ {
+ }
+
+ /**
+ */
+ public function read($id)
+ {
+ $file = $this->_path . '/sess_' . $id;
+ $session_data = @file_get_contents($file);
+ if (($session_data === false) && $this->_logger) {
+ $this->_logger->log('Unable to read file: ' . $file, 'ERR');
+ }
+
+ return strval($session_data);
+ }
+
+ /**
+ */
+ public function write($id, $session_data)
+ {
+ return false;
+ }
+
+ /**
+ */
+ public function destroy($id)
+ {
+ return false;
+ }
+
+ /**
+ */
+ public function gc($maxlifetime = 300)
+ {
+ return false;
+ }
+
+ /**
+ */
+ public function getSessionIDs()
+ {
+ $sessions = array();
+
+ try {
+ $di = new DirectoryIterator($this->_path);
+ } catch (UnexpectedValueException $e) {
+ return $sessions;
+ }
+
+ foreach ($di as $val) {
+ /* Make sure we're dealing with files that start with sess_. */
+ if ($val->isFile() &&
+ (strpos($val->getFilename(), 'sess_') === 0)) {
+ $sessions[] = substr($val->getFilename(), strlen('sess_'));
+ }
+ }
+
+ return $sessions;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * SessionHandler storage implementation for an external save handler defined
+ * via configuration parameters.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package SessionHandler
+ */
+class Horde_SessionHandler_Storage_External extends Horde_SessionHandler_Storage
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params Required parameters:
+ * <pre>
+ * close - (callback) See session_set_save_handler().
+ * destroy - (callback) See session_set_save_handler().
+ * gc - (callback) See session_set_save_handler().
+ * open - (callback) See session_set_save_handler().
+ * read - (callback) See session_set_save_handler().
+ * write - (callback) See session_set_save_handler().
+ * </pre>
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct(array $params = array())
+ {
+ foreach (array('open', 'close', 'read', 'write', 'destroy', 'gc') as $val) {
+ if (!isset($params[$val])) {
+ throw new InvalidArgumentException('Missing parameter: ' . $val);
+ }
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ */
+ public function open($save_path = null, $session_name = null)
+ {
+ call_user_func($this->_params['open'], $save_path, $session_name);
+ }
+
+ /**
+ */
+ public function close()
+ {
+ call_user_func($this->_params['close']);
+ }
+
+ /**
+ */
+ public function read($id)
+ {
+ return call_user_func($this->_params['read']);
+ }
+
+ /**
+ */
+ public function write($id, $session_data)
+ {
+ return call_user_func($this->_params['write'], $id, $session_data);
+ }
+
+ /**
+ */
+ public function destroy($id)
+ {
+ return call_user_func($this->_params['destroy'], $id);
+ }
+
+ /**
+ */
+ public function gc($maxlifetime = 300)
+ {
+ return call_user_func($this->_params['gc'], $maxlifetime);
+ }
+
+ /**
+ * Get a list of the valid session identifiers.
+ *
+ * @throws Horde_SessionHandler_Exception
+ */
+ public function getSessionIDs()
+ {
+ throw new Horde_SessionHandler_Exception('Driver does not support listing session IDs.');
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Horde_SessionHandler implementation for memcache.
+ *
+ * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Rong-En Fan <rafan@infor.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package SessionHandler
+ */
+class Horde_SessionHandler_Storage_Memcache extends Horde_SessionHandler_Storage
+{
+ /**
+ * Memcache object.
+ *
+ * @var Horde_Memcache
+ */
+ protected $_memcache;
+
+ /**
+ * Current session ID.
+ *
+ * @var string
+ */
+ protected $_id;
+
+ /**
+ * The ID used for session tracking.
+ *
+ * @var string
+ */
+ protected $_trackID = 'horde_memcache_sessions_track';
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Parameters:
+ * <pre>
+ * 'memcache' - (Horde_Memcache) [REQUIRED] A memcache object.
+ * 'track' - (boolean) Track active sessions?
+ * 'track_lifetime' - (integer) The number of seconds after which tracked
+ * sessions will be treated as expired.
+ * </pre>
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct(array $params = array())
+ {
+ if (empty($params['memcache'])) {
+ throw new InvalidArgumentException('Missing memcache argument.');
+ }
+
+ $this->_memcache = $params['memcache'];
+ unset($params['memcache']);
+
+ parent::__construct($params);
+
+ if (empty($this->_params['track_lt'])) {
+ $this->_params['track_lt'] = ini_get('session.gc_maxlifetime');
+ }
+
+ if (!empty($this->_params['track']) && (rand(0, 999) == 0)) {
+ register_shutdown_function(array($this, 'trackGC'));
+ }
+ }
+
+ /**
+ */
+ public function open($save_path = null, $session_name = null)
+ {
+ }
+
+ /**
+ */
+ public function close()
+ {
+ if (isset($this->_id)) {
+ $this->_memcache->unlock($this->_id);
+ }
+ }
+
+ /**
+ */
+ public function read($id)
+ {
+ if (!$this->readonly) {
+ $this->_memcache->lock($id);
+ }
+ $result = $this->_memcache->get($id);
+
+ if ($result === false) {
+ if (!$this->readonly) {
+ $this->_memcache->unlock($id);
+ }
+
+ if ($result === false) {
+ if ($this->_logger) {
+ $this->_logger->log('Error retrieving session data (id = ' . $id . ')', 'DEBUG');
+ }
+ return false;
+ }
+ }
+
+ if (!$this->readonly) {
+ $this->_id = $id;
+ }
+
+ if ($this->_logger) {
+ $this->_logger->log('Read session data (id = ' . $id . ')', 'DEBUG');
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ public function write($id, $session_data)
+ {
+ if (!empty($this->_params['track'])) {
+ // Do a replace - the only time it should fail is if we are
+ // writing a session for the first time. If that is the case,
+ // update the session tracker.
+ $res = $this->_memcache->replace($id, $session_data);
+ $track = !$res;
+ } else {
+ $res = $track = false;
+ }
+
+ if (!$res &&
+ !$this->_memcache->set($id, $session_data)) {
+ if ($this->_logger) {
+ $this->_logger->log('Error writing session data (id = ' . $id . ')', 'ERR');
+ }
+ return false;
+ }
+
+ if ($track) {
+ $this->_memcache->lock($this->_trackID);
+ $ids = $this->_memcache->get($this->_trackID);
+ if ($ids === false) {
+ $ids = array();
+ }
+
+ $ids[$id] = time();
+ $this->_memcache->set($this->_trackID, $ids);
+ $this->_memcache->unlock($this->_trackID);
+ }
+
+ if ($this->_logger) {
+ $this->_logger->log('Wrote session data (id = ' . $id . ')', 'DEBUG');
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ public function destroy($id)
+ {
+ $result = $this->_memcache->delete($id);
+ $this->_memcache->unlock($id);
+
+ if ($result === false) {
+ if ($this->_logger) {
+ $this->_logger->log('Failed to delete session (id = ' . $id . ')', 'DEBUG');
+ }
+ return false;
+ }
+
+ if (!empty($this->_params['track'])) {
+ $this->_memcache->lock($this->_trackID);
+ $ids = $this->_memcache->get($this->_trackID);
+ if ($ids !== false) {
+ unset($ids[$id]);
+ $this->_memcache->set($this->_trackID, $ids);
+ }
+ $this->_memcache->unlock($this->_trackID);
+ }
+
+ if ($this->_logger) {
+ $this->_logger->log('Deleted session data (id = ' . $id . ')', 'DEBUG');
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ public function gc($maxlifetime = 300)
+ {
+ // Memcache does its own garbage collection.
+ return true;
+ }
+
+ /**
+ */
+ public function getSessionIDs()
+ {
+ if (empty($this->_params['track'])) {
+ throw new Horde_SessionHandler_Exception('Memcache session tracking not enabled.');
+ }
+
+ $this->trackGC();
+
+ $ids = $this->_memcache->get($this->_trackID);
+
+ return ($ids === false)
+ ? array()
+ : array_keys($ids);
+ }
+
+ /**
+ * Do garbage collection for session tracking information.
+ */
+ public function trackGC()
+ {
+ $this->_memcache->lock($this->_trackID);
+ $ids = $this->_memcache->get($this->_trackID);
+ if (empty($ids)) {
+ $this->_memcache->unlock($this->_trackID);
+ return;
+ }
+
+ $tstamp = time() - $this->_params['track_lt'];
+ $alter = false;
+
+ foreach ($ids as $key => $val) {
+ if ($tstamp > $val) {
+ unset($ids[$key]);
+ $alter = true;
+ }
+ }
+
+ if ($alter) {
+ $this->_memcache->set($this->_trackID, $ids);
+ }
+
+ $this->_memcache->unlock($this->_trackID);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * SessionHandler storage implementation for SQL databases.
+ *
+ * Uses the following SQL table structure:
+ * <pre>
+ * CREATE TABLE horde_sessionhandler (
+ * VARCHAR(32) NOT NULL,
+ * session_lastmodified INT NOT NULL,
+ * session_data LONGBLOB,
+ * -- Or, on some DBMS systems:
+ * -- session_data IMAGE,
+ *
+ * PRIMARY KEY (session_id)
+ * );
+ *
+ * CREATE INDEX session_lastmodified_idx ON horde_sessionhandler (session_lastmodified);
+ * </pre>
+ *
+ * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package SessionHandler
+ */
+class Horde_SessionHandler_Storage_Sql extends Horde_SessionHandler_Storage
+{
+ /**
+ * Handle for the current database connection.
+ *
+ * @var Horde_Db_Adapter
+ */
+ protected $_db;
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Parameters:
+ * <pre>
+ * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
+ * 'table' - (string) The name of the sessions table.
+ * DEFAULT: 'horde_sessionhandler'
+ * </pre>
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct(array $params = array())
+ {
+ if (!isset($params['db'])) {
+ throw new InvalidArgumentException('Missing db parameter.');
+ }
+ $this->_db = $params['db'];
+ unset($params['db']);
+
+ parent::__construct(array_merge(array(
+ 'table' => 'horde_sessionhandler'
+ ), $params));
+ }
+
+ /**
+ */
+ public function open($save_path = null, $session_name = null)
+ {
+ }
+
+ /**
+ */
+ public function close()
+ {
+ /* Close any open transactions. */
+ try {
+ $this->_db->commitDbTransaction();
+ } catch (Horde_Db_Exception $e) {
+ throw new Horde_SessionHandler_Exception($e);
+ }
+ }
+
+ /**
+ */
+ public function read($id)
+ {
+ /* Begin a transaction. */
+ // TODO: Rowlocking in Mysql
+ $this->_db->beginDbTransaction();
+
+ /* Build query. */
+ $query = sprintf('SELECT session_data FROM %s WHERE session_id = ?',
+ $this->_params['table']);
+ $values = array($id);
+
+ /* Execute the query. */
+ try {
+ return $this->_db->selectValue($query, $values);
+ } catch (Horde_Db_Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ */
+ public function write($id, $session_data)
+ {
+ /* Build the SQL query. */
+ $query = sprintf('SELECT session_id FROM %s WHERE session_id = ?',
+ $this->_params['table']);
+ $values = array($id);
+
+ /* Execute the query. */
+ try {
+ $result = $this->_db->selectValue($query, $values);
+ } catch (Horde_Db_Exception $e) {
+ return false;
+ }
+
+ /* Build the replace SQL query. */
+ $query = sprintf('REPLACE INTO %s ' .
+ '(session_id, session_data, session_lastmodified) ' .
+ 'VALUES (?, ?, ?)',
+ $this->_params['table']);
+ $values = array(
+ $id,
+ $session_data,
+ time()
+ );
+
+ /* Execute the replace query. */
+ try {
+ $this->_db->update($query, $values);
+ $this->_db->commitDbTransaction();
+ } catch (Horde_Db_Exception $e) {
+ $this->_db->rollbackDbTransaction();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ public function destroy($id)
+ {
+ /* Build the SQL query. */
+ $query = sprintf('DELETE FROM %s WHERE session_id = ?',
+ $this->_params['table']);
+ $values = array($id);
+
+ /* Execute the query. */
+ try {
+ $this->_db->delete($query, $values);
+ $this->_db->commitDbTransaction();
+ } catch (Horde_Db_Exception $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ public function gc($maxlifetime = 300)
+ {
+ /* Build the SQL query. */
+ $query = sprintf('DELETE FROM %s WHERE session_lastmodified < ?',
+ $this->_params['table']);
+ $values = array(time() - $maxlifetime);
+
+ /* Execute the query. */
+ try {
+ $this->_db->delete($query, $values);
+ } catch (Horde_Db_Exception $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ public function getSessionIDs()
+ {
+ $this->open();
+
+ /* Build the SQL query. */
+ $query = sprintf('SELECT session_id FROM %s' .
+ ' WHERE session_lastmodified >= ?',
+ $this->_params['table']);
+ $values = array(time() - ini_get('session.gc_maxlifetime'));
+
+ /* Execute the query. */
+ try {
+ return $this->_db->selectValues($query, $values);
+ } catch (Horde_Db_Exception $e) {
+ return array();
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * SessionHandler storage implementation that will loop through a list of
+ * storage drivers to handle the session information.
+ * This driver allows for use of caching backends on top of persistent
+ * backends, for example.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package SessionHandler
+ */
+class Horde_SessionHandler_Storage_Stack extends Horde_SessionHandler_Storage
+{
+ /**
+ * Stack of storage objects.
+ *
+ * @var array
+ */
+ protected $_stack = array();
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Parameters:
+ * <pre>
+ * stack - (array) [REQUIRED] A list of storage objects to loop
+ * through, in order of priority. The last entry is considered
+ * the "master" server.
+ * </pre>
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct(array $params = array())
+ {
+ if (!isset($params['stack'])) {
+ throw new InvalidArgumentException('Missing stack parameter.');
+ }
+
+ $this->_stack = $params['stack'];
+ unset($params['stack']);
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Set the logger object.
+ *
+ * @param Horde_Log_Logger $log The logger instance.
+ */
+ public function setLogger(Horde_Log_Logger $log)
+ {
+ parent::setLogger($log);
+
+ foreach ($this->_stack as $ob) {
+ $ob->setLogger($log);
+ }
+ }
+
+ /**
+ */
+ public function open($save_path = null, $session_name = null)
+ {
+ foreach ($this->_stack as $val) {
+ $val->open($save_path, $session_name);
+ }
+ }
+
+ /**
+ */
+ public function close()
+ {
+ foreach ($this->_stack as $val) {
+ $val->close();
+ }
+ }
+
+ /**
+ */
+ public function read($id)
+ {
+ foreach ($this->_stack as $val) {
+ $result = $val->read($id);
+ if ($result === false) {
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ public function write($id, $session_data)
+ {
+ /* Do writes in *reverse* order - it is OK if a write to one of the
+ * non-master backends fails. */
+ $master = true;
+
+ foreach (array_reverse($this->_stack) as $val) {
+ $result = $val->write($id, $session_data);
+ if ($result === false) {
+ if ($master) {
+ return false;
+ }
+ /* Attempt to invalidate cache if write failed. */
+ $val->destroy($id);
+ }
+ $master = false;
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ public function destroy($id)
+ {
+ /* Only report success from master. */
+ $master = $success = true;
+
+ foreach (array_reverse($this->_stack) as $val) {
+ $result = $val->destroy($id);
+ if ($master && ($result === false)) {
+ $success = false;
+ }
+ $master = false;
+ }
+
+ return $success;
+ }
+
+ /**
+ */
+ public function gc($maxlifetime = 300)
+ {
+ /* Only report GC results from master. */
+ foreach ($this->_stack as $val) {
+ $result = $val->gc($maxlifetime);
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ public function getSessionIDs()
+ {
+ /* Grab session ID list from the master. */
+ $ob = end($this->_stack);
+ return $ob->getSessionIDs();
+ }
+
+}
<api>beta</api>
</stability>
<license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>
+ <notes>* Abstracted storage-specific code into 'Storage' drivers.
* Removed LDAP driver
* Abstracted memcache persistent-backend code into 'Stack' driver.
* Renamed 'none' driver to 'Builtin'.
<dir name="lib">
<dir name="Horde">
<dir name="SessionHandler">
- <file name="Builtin.php" role="php" />
+ <dir name="Storage">
+ <file name="Builtin.php" role="php" />
+ <file name="External.php" role="php" />
+ <file name="Memcache.php" role="php" />
+ <file name="Sql.php" role="php" />
+ <file name="Stack.php" role="php" />
+ </dir> <!-- //lib/Horde/SessionHandler/Storage -->
<file name="Exception.php" role="php" />
- <file name="External.php" role="php" />
- <file name="Memcache.php" role="php" />
- <file name="Sql.php" role="php" />
- <file name="Stack.php" role="php" />
+ <file name="Storage.php" role="php" />
</dir> <!-- //lib/Horde/SessionHandler -->
<file name="SessionHandler.php" role="php" />
</dir> <!-- //lib/Horde -->
<phprelease>
<filelist>
<install as="Horde/SessionHandler.php" name="lib/Horde/SessionHandler.php" />
- <install as="Horde/SessionHandler/Builtin.php" name="lib/Horde/SessionHandler/Builtin.php" />
+ <install as="Horde/SessionHandler/Storage/Builtin.php" name="lib/Horde/SessionHandler/Storage/Builtin.php" />
+ <install as="Horde/SessionHandler/Storage/External.php" name="lib/Horde/SessionHandler/Storage/External.php" />
+ <install as="Horde/SessionHandler/Storage/Memcache.php" name="lib/Horde/SessionHandler/Storage/Memcache.php" />
+ <install as="Horde/SessionHandler/Storage/Sql.php" name="lib/Horde/SessionHandler/Storage/Sql.php" />
+ <install as="Horde/SessionHandler/Storage/Stack.php" name="lib/Horde/SessionHandler/Storage/Stack.php" />
<install as="Horde/SessionHandler/Exception.php" name="lib/Horde/SessionHandler/Exception.php" />
- <install as="Horde/SessionHandler/External.php" name="lib/Horde/SessionHandler/External.php" />
- <install as="Horde/SessionHandler/Memcache.php" name="lib/Horde/SessionHandler/Memcache.php" />
- <install as="Horde/SessionHandler/Sql.php" name="lib/Horde/SessionHandler/Sql.php" />
- <install as="Horde/SessionHandler/Stack.php" name="lib/Horde/SessionHandler/Stack.php" />
+ <install as="Horde/SessionHandler/Storage.php" name="lib/Horde/SessionHandler/Storage.php" />
</filelist>
</phprelease>
<changelog>