From: Ben Klang Date: Sun, 3 Oct 2010 18:18:30 +0000 (-0400) Subject: Rebuild SessionHandler factory X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=1948e4f501a18c5856cbe45caf09267c0020e036;p=horde.git Rebuild SessionHandler factory --- diff --git a/framework/Core/lib/Horde/Core/Binder/SessionHandler.php b/framework/Core/lib/Horde/Core/Binder/SessionHandler.php deleted file mode 100644 index b4741d42a..000000000 --- a/framework/Core/lib/Horde/Core/Binder/SessionHandler.php +++ /dev/null @@ -1,106 +0,0 @@ -getInstance('Horde_Db_Adapter'); - } elseif (strcasecmp($driver, 'Memcache') === 0) { - $params['memcache'] = $injector->getInstance('Horde_Memcache'); - } elseif (strcasecmp($driver, 'Ldap') === 0) { - $params['ldap'] = $injector->getInstances('Horde_Core_Factory_Ldap')->getLdap('horde', 'sessionhandler'); - } - - $logger = $injector->getInstance('Horde_Log_Logger'); - - if (!empty($conf['sessionhandler']['memcache']) && - (strcasecmp($driver, 'Builtin') != 0) && - (strcasecmp($driver, 'Memcache') != 0)) { - $params = 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 - )) - ) - ) - ); - $driver = 'Stack'; - } - - $params['logger'] = $logger; - $params['parse'] = array($this, 'readSessionData'); - - return Horde_SessionHandler::factory($driver, $params); - } - - public function equals(Horde_Injector_Binder $binder) - { - return false; - } - - /** - * Reads session data to determine if it contains Horde authentication - * credentials. - * - * @param string $session_data The session data. - * - * @return array An array of the user's sesion information if - * authenticated or false. The following information is - * returned: userid, timestamp, remoteAddr, browser, apps. - */ - public function readSessionData($session_data) - { - if (empty($session_data) || - (($pos = strpos($session_data, 'horde_auth|')) === false)) { - return false; - } - - $pos += 11; - $endpos = $pos + 1; - - while ($endpos !== false) { - $endpos = strpos($session_data, '|', $endpos); - $data = @unserialize(substr($session_data, $pos, $endpos)); - if (is_array($data)) { - return empty($data) - ? false - : array( - 'apps' => empty($data['app']) ? array('horde') : array_keys($data['app']), - 'browser' => $data['browser'], - 'remoteAddr' => $data['remoteAddr'], - 'timestamp' => $data['timestamp'], - 'userid' => $data['userId'] - ); - } - ++$endpos; - } - - return false; - } - -} diff --git a/framework/Core/lib/Horde/Core/Factory/SessionHandler.php b/framework/Core/lib/Horde/Core/Factory/SessionHandler.php new file mode 100644 index 000000000..79b9b1be7 --- /dev/null +++ b/framework/Core/lib/Horde/Core/Factory/SessionHandler.php @@ -0,0 +1,124 @@ + + * + * @category Horde + * @package Core + */ +class Horde_Core_Factory_SessionHandler +{ + /** + * Attempts to return a concrete instance based on $driver. + * + * @param string $driver The type of concrete subclass to return + * (case-insensitive). + * @param array $params A hash containing any additional configuration or + * connection parameters a subclass might need. + * + * @return Horde_SessionHandler_Driver The newly created concrete + * instance. + * @throws Horde_SessionHandler_Exception + */ + public function create(Horde_Injector $injector) + { + global $conf; + + if (empty($conf['sessionhandler']['type'])) { + $driver = 'Builtin'; + } else { + $driver = $conf['sessionhandler']['type']; + if (!strcasecmp($driver, 'None')) { + $driver = 'Builtin'; + } + } + $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) { + $params['ldap'] = $injector->getInstances('Horde_Core_Factory_Ldap')->getLdap('horde', 'sessionhandler'); + } + + $logger = $injector->getInstance('Horde_Log_Logger'); + + if (!empty($conf['sessionhandler']['memcache']) && + (strcasecmp($driver, 'Builtin') != 0) && + (strcasecmp($driver, 'Memcache') != 0)) { + $params = 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 + )) + ) + ) + ); + $driver = 'Stack'; + } + + $params['logger'] = $logger; + $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); + } + + /** + * Reads session data to determine if it contains Horde authentication + * credentials. + * + * @param string $session_data The session data. + * + * @return array An array of the user's sesion information if + * authenticated or false. The following information is + * returned: userid, timestamp, remoteAddr, browser, apps. + */ + public function readSessionData($session_data) + { + if (empty($session_data) || + (($pos = strpos($session_data, 'horde_auth|')) === false)) { + return false; + } + + $pos += 11; + $endpos = $pos + 1; + + while ($endpos !== false) { + $endpos = strpos($session_data, '|', $endpos); + $data = @unserialize(substr($session_data, $pos, $endpos)); + if (is_array($data)) { + return empty($data) + ? false + : array( + 'apps' => empty($data['app']) ? array('horde') : array_keys($data['app']), + 'browser' => $data['browser'], + 'remoteAddr' => $data['remoteAddr'], + 'timestamp' => $data['timestamp'], + 'userid' => $data['userId'] + ); + } + ++$endpos; + } + + return false; + } + +} diff --git a/framework/Core/lib/Horde/Registry.php b/framework/Core/lib/Horde/Registry.php index a98bd7aac..2a010cff3 100644 --- a/framework/Core/lib/Horde/Registry.php +++ b/framework/Core/lib/Horde/Registry.php @@ -279,7 +279,6 @@ class Horde_Registry 'Horde_Mime_Viewer' => new Horde_Core_Binder_MimeViewer(), 'Horde_Notification' => new Horde_Core_Binder_Notification(), 'Horde_Prefs_Identity' => new Horde_Core_Binder_Identity(), - 'Horde_SessionHandler' => new Horde_Core_Binder_SessionHandler(), 'Horde_Share_Factory' => new Horde_Core_Binder_ShareFactory(), 'Horde_Template' => new Horde_Core_Binder_Template(), 'Horde_Text_Filter' => new Horde_Core_Binder_TextFilter(), @@ -1677,7 +1676,7 @@ class Horde_Registry /* We want to create an instance here, not get, since we may be * destroying the previous instances in the page. */ - $this->sessionHandler = $GLOBALS['injector']->createInstance('Horde_SessionHandler'); + $this->sessionHandler = $GLOBALS['injector']->createInstance('Horde_Core_Factory_SessionHandler'); if ($start) { session_start(); diff --git a/framework/Core/package.xml b/framework/Core/package.xml index 0962f58ea..35fd3217b 100644 --- a/framework/Core/package.xml +++ b/framework/Core/package.xml @@ -24,7 +24,7 @@ Application Framework. yes 2010-10-03 - + 0.1.0 0.1.0 @@ -128,7 +128,6 @@ Application Framework. - @@ -169,6 +168,7 @@ Application Framework. + @@ -447,7 +447,6 @@ Application Framework. - @@ -484,6 +483,7 @@ Application Framework. + diff --git a/framework/SessionHandler/lib/Horde/SessionHandler.php b/framework/SessionHandler/lib/Horde/SessionHandler.php index e6e578a41..2a69d8232 100644 --- a/framework/SessionHandler/lib/Horde/SessionHandler.php +++ b/framework/SessionHandler/lib/Horde/SessionHandler.php @@ -1,42 +1,332 @@ * @author Michael Slusarz * @category Horde * @package SessionHandler */ -class Horde_SessionHandler +abstract class Horde_SessionHandler { /** - * Attempts to return a concrete instance based on $driver. + * Hash containing connection parameters. * - * @param string $driver The type of concrete subclass to return - * (case-insensitive). - * @param array $params A hash containing any additional configuration or - * connection parameters a subclass might need. + * @var array + */ + protected $_params = array(); + + /** + * Initial session data signature. + * + * @var string + */ + protected $_sig; + + /** + * Force saving the session data? + * + * @var boolean + */ + protected $_force = false; + + /** + * Has a connection been made to the backend? + * + * @var boolean + */ + protected $_connected = false; + + /** + * A logger instance. + * + * @var Horde_Log_Logger + */ + protected $_logger; + + /** + * Session variable name. + * + * @var string + */ + protected $_session = 'horde_sh'; + + /** + * Constructor. + * + * @param array $params Parameters: + *
+     * 'logger' - (Horde_Log_Logger) A logger instance.
+     *            DEFAULT: No logging
+     * 'noset' - (boolean) If true, don't set the save handler.
+     *           DEFAULT: false
+     * 'parse' - (callback) A callback function that parses session
+     *           information into an array. Is passed the raw session data
+     *           as the only argument; expects either false or an array of
+     *           session data as a return.
+     *           DEFAULT: No
+     * 
+ */ + public function __construct(array $params = array()) + { + $params = array_merge($this->_params, $params); + + if (isset($params['logger'])) { + $this->_logger = $params['logger']; + unset($params['logger']); + } + + $this->_params = $params; + + register_shutdown_function(array($this, 'shutdown')); + + if (empty($this->_params['noset'])) { + ini_set('session.save_handler', 'user'); + session_set_save_handler( + array($this, 'open'), + array($this, 'close'), + array($this, 'read'), + array($this, 'write'), + array($this, 'destroy'), + array($this, 'gc') + ); + } + } + + /** + * Destructor. + */ + public function __destruct() + { + /* This is necessary as of PHP 5.0.5 because objects are not available + * when the write() handler is called at the end of a session + * access. */ + session_write_close(); + } + + /** + * Shutdown function. + * + * Used to determine if we need to write the session to avoid a session + * timeout, even though the session is unchanged. + * Theory: On initial login, set the current time plus half of the max + * lifetime in the session. Then check this timestamp before saving. + * If we exceed, force a write of the session and set a new timestamp. + * Why half the maxlifetime? It guarantees that if we are accessing the + * server via a periodic mechanism (think folder refreshing in IMP) that + * we will catch this refresh. + */ + public function shutdown() + { + $curr_time = time(); + + if (!isset($_SESSION[$this->_session]) || + ($curr_time >= $_SESSION[$this->_session])) { + $_SESSION[$this->_session] = $curr_time + (ini_get('session.gc_maxlifetime') / 2); + $this->_force = true; + } + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. + * + * @return boolean True on success, false otherwise. + */ + public function open($save_path = null, $session_name = null) + { + if (!$this->_connected) { + try { + $this->_open($save_path, $session_name); + } catch (Horde_SessionHandler_Exception $e) { + if ($this->_logger) { + $this->_logger->log($e, 'ERR'); + } + return false; + } + + $this->_connected = true; + } + + return true; + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. * - * @return Horde_SessionHandler_Driver The newly created concrete - * instance. * @throws Horde_SessionHandler_Exception */ - static public function factory($driver, array $params = array()) + abstract protected function _open($save_path = null, $session_name = null); + + /** + * Close the backend. + * + * @return boolean True on success, false otherwise. + */ + public function close() { - $driver = basename(strtolower($driver)); - $class = __CLASS__ . '_' . ucfirst($driver); + try { + $this->_close(); + } catch (Horde_SessionHandler_Exception $e) { + if ($this->_logger) { + $this->_logger->log($e, 'ERR'); + } + return false; + } + + $this->_connected = false; + return true; + } + + /** + * 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(). + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + public function read($id) + { + $result = $this->_read($id); + $this->_sig = md5($result); + return $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(). + * + * @param string $id The session identifier. + * @param string $session_data The session data. + * + * @return boolean True on success, false otherwise. + */ + public function write($id, $session_data) + { + if (!$this->_force && ($this->_sig == md5($session_data))) { + if ($this->_logger) { + $this->_logger->log('Session data unchanged (id = ' . $id . ')', 'DEBUG'); + } + return true; + } + + return $this->_write($id, $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. + */ + 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(). + * + * @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 session data read-only. + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + protected function _readOnly($id) + { + return $this->read($id); + } + + /** + * Get a list of the valid session identifiers. + * + * @return array A list of valid session identifiers. + * @throws Horde_SessionHandler_Exception + */ + abstract public function getSessionIDs(); + + /** + * Returns a list of authenticated users and data about their session. + * + * @return array For authenticated users, the sessionid as a key and the + * session information as value. If no parsing function + * was provided, will always return an empty array. + * @throws Horde_SessionHandler_Exception + */ + public function getSessionsInfo() + { + $info = array(); + + if (empty($this->_params['parse']) || + !is_callable($this->_params['parse'])) { + return $info; + } + + $sessions = $this->getSessionIDs(); + + foreach ($sessions as $id) { + try { + $data = $this->_readOnly($id); + } catch (Horde_SessionHandler_Exception $e) { + continue; + } - if (class_exists($class)) { - return new $class($params); + $data = call_user_func($this->_params['parse'], $data); + if ($data !== false) { + $info[$id] = $data; + } } - throw new Horde_SessionHandler_Exception('Driver not found: ' . $driver); + return $info; } -} +} \ No newline at end of file diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Base.php b/framework/SessionHandler/lib/Horde/SessionHandler/Base.php deleted file mode 100644 index 10d65fb4d..000000000 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Base.php +++ /dev/null @@ -1,332 +0,0 @@ - - * @category Horde - * @package SessionHandler - */ -abstract class Horde_SessionHandler_Base -{ - /** - * Hash containing connection parameters. - * - * @var array - */ - protected $_params = array(); - - /** - * Initial session data signature. - * - * @var string - */ - protected $_sig; - - /** - * Force saving the session data? - * - * @var boolean - */ - protected $_force = false; - - /** - * Has a connection been made to the backend? - * - * @var boolean - */ - protected $_connected = false; - - /** - * A logger instance. - * - * @var Horde_Log_Logger - */ - protected $_logger; - - /** - * Session variable name. - * - * @var string - */ - protected $_session = 'horde_sh'; - - /** - * Constructor. - * - * @param array $params Parameters: - *
-     * 'logger' - (Horde_Log_Logger) A logger instance.
-     *            DEFAULT: No logging
-     * 'noset' - (boolean) If true, don't set the save handler.
-     *           DEFAULT: false
-     * 'parse' - (callback) A callback function that parses session
-     *           information into an array. Is passed the raw session data
-     *           as the only argument; expects either false or an array of
-     *           session data as a return.
-     *           DEFAULT: No
-     * 
- */ - public function __construct(array $params = array()) - { - $params = array_merge($this->_params, $params); - - if (isset($params['logger'])) { - $this->_logger = $params['logger']; - unset($params['logger']); - } - - $this->_params = $params; - - register_shutdown_function(array($this, 'shutdown')); - - if (empty($this->_params['noset'])) { - ini_set('session.save_handler', 'user'); - session_set_save_handler( - array($this, 'open'), - array($this, 'close'), - array($this, 'read'), - array($this, 'write'), - array($this, 'destroy'), - array($this, 'gc') - ); - } - } - - /** - * Destructor. - */ - public function __destruct() - { - /* This is necessary as of PHP 5.0.5 because objects are not available - * when the write() handler is called at the end of a session - * access. */ - session_write_close(); - } - - /** - * Shutdown function. - * - * Used to determine if we need to write the session to avoid a session - * timeout, even though the session is unchanged. - * Theory: On initial login, set the current time plus half of the max - * lifetime in the session. Then check this timestamp before saving. - * If we exceed, force a write of the session and set a new timestamp. - * Why half the maxlifetime? It guarantees that if we are accessing the - * server via a periodic mechanism (think folder refreshing in IMP) that - * we will catch this refresh. - */ - public function shutdown() - { - $curr_time = time(); - - if (!isset($_SESSION[$this->_session]) || - ($curr_time >= $_SESSION[$this->_session])) { - $_SESSION[$this->_session] = $curr_time + (ini_get('session.gc_maxlifetime') / 2); - $this->_force = true; - } - } - - /** - * Open the backend. - * - * @param string $save_path The path to the session object. - * @param string $session_name The name of the session. - * - * @return boolean True on success, false otherwise. - */ - public function open($save_path = null, $session_name = null) - { - if (!$this->_connected) { - try { - $this->_open($save_path, $session_name); - } catch (Horde_SessionHandler_Exception $e) { - if ($this->_logger) { - $this->_logger->log($e, 'ERR'); - } - return false; - } - - $this->_connected = true; - } - - return true; - } - - /** - * 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(); - } catch (Horde_SessionHandler_Exception $e) { - if ($this->_logger) { - $this->_logger->log($e, 'ERR'); - } - return false; - } - - $this->_connected = false; - return true; - } - - /** - * 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(). - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - public function read($id) - { - $result = $this->_read($id); - $this->_sig = md5($result); - return $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(). - * - * @param string $id The session identifier. - * @param string $session_data The session data. - * - * @return boolean True on success, false otherwise. - */ - public function write($id, $session_data) - { - if (!$this->_force && ($this->_sig == md5($session_data))) { - if ($this->_logger) { - $this->_logger->log('Session data unchanged (id = ' . $id . ')', 'DEBUG'); - } - return true; - } - - return $this->_write($id, $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. - */ - 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(). - * - * @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 session data read-only. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _readOnly($id) - { - return $this->read($id); - } - - /** - * Get a list of the valid session identifiers. - * - * @return array A list of valid session identifiers. - * @throws Horde_SessionHandler_Exception - */ - abstract public function getSessionIDs(); - - /** - * Returns a list of authenticated users and data about their session. - * - * @return array For authenticated users, the sessionid as a key and the - * session information as value. If no parsing function - * was provided, will always return an empty array. - * @throws Horde_SessionHandler_Exception - */ - public function getSessionsInfo() - { - $info = array(); - - if (empty($this->_params['parse']) || - !is_callable($this->_params['parse'])) { - return $info; - } - - $sessions = $this->getSessionIDs(); - - foreach ($sessions as $id) { - try { - $data = $this->_readOnly($id); - } catch (Horde_SessionHandler_Exception $e) { - continue; - } - - $data = call_user_func($this->_params['parse'], $data); - if ($data !== false) { - $info[$id] = $data; - } - } - - return $info; - } - -} diff --git a/framework/SessionHandler/package.xml b/framework/SessionHandler/package.xml index cce1f5ec4..d9a789f7b 100644 --- a/framework/SessionHandler/package.xml +++ b/framework/SessionHandler/package.xml @@ -23,8 +23,8 @@ slusarz@horde.org yes - 2010-05-20 - + 2010-10-03 + 0.1.0 0.1.0 @@ -50,17 +50,15 @@ - - - + - - + + @@ -99,10 +97,8 @@ - - @@ -172,17 +168,17 @@ Initial release as a PEAR package beta beta - 2010-05-20 + 2010-10-03 LGPL * Abstracted memcache persistent-backend code into 'Stack' driver. - * Renamed 'none' driver to 'Builtin'. - * Now throws Horde_SessionHandler_Exception. - * Split driver code into abstract class. - * Use horde/Db to access SQL databases. - * Do not rely on Horde::logMessage(). - * Removed Horde-specific session counting script. - * Initial Horde 4 package. +* Renamed 'none' driver to 'Builtin'. +* Now throws Horde_SessionHandler_Exception. +* Split driver code into abstract class. +* Use horde/Db to access SQL databases. +* Do not rely on Horde::logMessage(). +* Removed Horde-specific session counting script. +* Initial Horde 4 package.