From 24b4c87cc434f9e3bda6902fd705e0d234f13e57 Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Thu, 28 Jun 2012 14:56:10 -0400 Subject: [PATCH] Progress on session configuration. --- module/VuFind/src/VuFind/Account/Manager.php | 4 +- module/VuFind/src/VuFind/Bootstrap.php | 18 +- .../src/VuFind/Session/AbstractBase.php | 148 +++++++++++++++++ module/VuFind/src/VuFind/Session/Database.php | 122 ++++++++++++++ module/VuFind/src/VuFind/Session/File.php | 157 ++++++++++++++++++ module/VuFind/src/VuFind/Session/Memcache.php | 116 +++++++++++++ 6 files changed, 555 insertions(+), 10 deletions(-) create mode 100644 module/VuFind/src/VuFind/Session/AbstractBase.php create mode 100644 module/VuFind/src/VuFind/Session/Database.php create mode 100644 module/VuFind/src/VuFind/Session/File.php create mode 100644 module/VuFind/src/VuFind/Session/Memcache.php diff --git a/module/VuFind/src/VuFind/Account/Manager.php b/module/VuFind/src/VuFind/Account/Manager.php index f2ebed66d7f..4eeb3e9d63c 100644 --- a/module/VuFind/src/VuFind/Account/Manager.php +++ b/module/VuFind/src/VuFind/Account/Manager.php @@ -27,7 +27,7 @@ */ namespace VuFind\Account; use VuFind\Auth\Factory as AuthFactory, VuFind\Config\Reader as ConfigReader, - Zend\Registry, Zend\Session, Zend\Session\Container as SessionContainer; + Zend\Registry, Zend\Session\Container as SessionContainer; /** * Wrapper class for handling logged-in user in session. @@ -164,7 +164,7 @@ class Manager // Destroy the session for good measure, if requested. if ($destroy) { - Session::destroy(); + Registry::getInstance()->get('Zend_Session')->destroy(); } else { // If we don't want to destroy the session, we still need to empty it. // There should be a way to do this through Zend\Session, but there diff --git a/module/VuFind/src/VuFind/Bootstrap.php b/module/VuFind/src/VuFind/Bootstrap.php index d3f2e9483db..6c23596a2d3 100644 --- a/module/VuFind/src/VuFind/Bootstrap.php +++ b/module/VuFind/src/VuFind/Bootstrap.php @@ -32,7 +32,7 @@ use VuFind\Account\Manager as AccountManager, VuFind\Theme\Initializer as ThemeInitializer, VuFind\Translator\Factory as TranslatorFactory, Zend\Mvc\MvcEvent, Zend\Registry, Zend\Mvc\Router\Http\RouteMatch, - Zend\Translator\Translator; + Zend\Session\SessionManager, Zend\Translator\Translator; /** * VuFind Bootstrapper * @@ -307,12 +307,15 @@ class Bootstrap return; } - /* TODO: // Get session configuration: if (!isset($this->config->Session->type)) { throw new Exception('Cannot initialize session; configuration missing'); } + // Register a session manager: + $sessionManager = new SessionManager(); + Registry::getInstance()->set('Zend_Session', $sessionManager); + // Set up session handler (after manipulating the type setting for legacy // compatibility -- VuFind 1.x used MySQL instead of Database and had // "Session" as part of the configuration string): @@ -322,19 +325,18 @@ class Bootstrap if ($type == 'Mysql') { $type = 'Database'; } - $class = 'VF_Session_' . $type; - Zend_Session::setSaveHandler(new $class($this->config->Session)); + $class = 'VuFind\\Session\\' . $type; + $sessionManager->setSaveHandler(new $class($this->config->Session)); // Start up the session: - Zend_Session::start(); + $sessionManager->start(); // According to the PHP manual, session_write_close should always be // registered as a shutdown function when using an object as a session // handler: http://us.php.net/manual/en/function.session-set-save-handler.php - register_shutdown_function(array('Zend_Session', 'writeClose')); + register_shutdown_function(array($sessionManager, 'writeClose')); // Check user credentials: - VF_Account_Manager::getInstance()->checkForExpiredCredentials(); - */ + AccountManager::getInstance()->checkForExpiredCredentials(); } } \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Session/AbstractBase.php b/module/VuFind/src/VuFind/Session/AbstractBase.php new file mode 100644 index 00000000000..d36d4a0ff56 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/AbstractBase.php @@ -0,0 +1,148 @@ +<?php +/** + * Base class for session handling + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +namespace VuFind\Session; +use Zend\Session\SaveHandler\SaveHandlerInterface; + +/** + * Base class for session handling + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +abstract class AbstractBase implements SaveHandlerInterface +{ + public $lifetime = 3600; + + /** + * Constructor. + * + * @param Zend_Config $config Session configuration ([Session] section of + * config.ini) + */ + public function __construct($config) + { + if (isset($config->lifetime)) { + $this->lifetime = $config->lifetime; + } + } + + /** + * Open function, this works like a constructor in classes and is executed + * when the session is being opened. + * + * @param string $sess_path Session save path + * @param string $sess_name Session name + * + * @return void + */ + public function open($sess_path, $sess_name) + { + return true; + } + + /** + * Close function, this works like a destructor in classes and is executed + * when the session operation is done. + * + * @return void + */ + public function close() + { + return true; + } + + /** + * Read function must return string value always to make save handler work as + * expected. Return empty string if there is no data to read. + * + * @param string $sess_id The session ID to read + * + * @return string + */ + public function read($sess_id) + { + } + + /** + * Write function that is called when session data is to be saved. + * + * @param string $sess_id The current session ID + * @param string $data The session data to write + * + * @return void + */ + public function write($sess_id, $data) + { + } + + /** + * The destroy handler, this is executed when a session is destroyed with + * session_destroy() and takes the session id as its only parameter. + * + * IMPORTANT: The functionality defined in this method is global to all session + * mechanisms. If you override this method, be sure to still call + * parent::destroy() in addition to any new behavior. + * + * @param string $sess_id The session ID to destroy + * + * @return void + */ + public function destroy($sess_id) + { + /* TODO + $table = new VuFind_Model_Db_Search(); + $table->destroySession($sess_id); + */ + } + + /** + * The garbage collector, this is executed when the session garbage collector + * is executed and takes the max session lifetime as its only parameter. + * + * @param int $sess_maxlifetime Maximum session lifetime. + * + * @return void + */ + public function gc($sess_maxlifetime) + { + // how often does this get called (if at all)? + + // *** 08/Oct/09 - Greg Pendlebury + // Clearly this is being called. Production installs with + // thousands of sessions active are showing no old sessions. + // What I can't do is reproduce for testing. It might need the + // search delete code from 'destroy()' if it is not calling it. + // *** 09/Oct/09 - Greg Pendlebury + // Anecdotal testing Today and Yesterday seems to indicate destroy() + // is called by the garbage collector and everything is good. + // Something to keep in mind though. + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Session/Database.php b/module/VuFind/src/VuFind/Session/Database.php new file mode 100644 index 00000000000..cdec8176735 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/Database.php @@ -0,0 +1,122 @@ +<?php +/** + * Database session handler + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +namespace VuFind\Session; +use VuFind\Exception\SessionExpired as SessionExpiredException; + +/** + * Database session handler + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +class Database extends AbstractBase +{ + protected $table; + + /** + * Constructor. + * + * @param Zend_Config $config Session configuration ([Session] section of + * config.ini) + */ + public function __construct($config) + { + // Create database connection: + /* TODO: + $this->table = new VuFind_Model_Db_Session(); + */ + + // Call standard session initialization from this point. + parent::__construct($config); + } + + /** + * Read function must return string value always to make save handler work as + * expected. Return empty string if there is no data to read. + * + * @param string $sess_id The session ID to read + * + * @return string + */ + public function read($sess_id) + { + // Try to read the session, but destroy it if it has expired: + try { + return $this->table->readSession($sess_id, $this->lifetime); + } catch (SessionExpiredException $e) { + $this->destroy($sess_id); + return; + } + } + + /** + * Write function that is called when session data is to be saved. + * + * @param string $sess_id The current session ID + * @param string $data The session data to write + * + * @return void + */ + public function write($sess_id, $data) + { + $this->table->writeSession($sess_id, $data); + } + + /** + * The destroy handler, this is executed when a session is destroyed with + * session_destroy() and takes the session id as its only parameter. + * + * @param string $sess_id The session ID to destroy + * + * @return void + */ + public function destroy($sess_id) + { + // Perform standard actions required by all session methods: + parent::destroy($sess_id); + + // Now do database-specific destruction: + $this->table->destroySession($sess_id); + } + + /** + * The garbage collector, this is executed when the session garbage collector + * is executed and takes the max session lifetime as its only parameter. + * + * @param int $sess_maxlifetime Maximum session lifetime. + * + * @return void + */ + public function gc($sess_maxlifetime) + { + $this->table->garbageCollect($sess_maxlifetime); + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Session/File.php b/module/VuFind/src/VuFind/Session/File.php new file mode 100644 index 00000000000..e190e123da6 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/File.php @@ -0,0 +1,157 @@ +<?php +/** + * File-based session handler + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +namespace VuFind\Session; + +/** + * File-based session handler + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +class File extends AbstractBase +{ + protected $path; + + /** + * Constructor. + * + * @param Zend_Config $config Session configuration ([Session] section of + * config.ini) + */ + public function __construct($config) + { + // Set defaults if nothing set in config file. + if (isset($config->file_save_path)) { + $this->path = $config->file_save_path; + } else { + $tempdir = function_exists('sys_get_temp_dir') + ? sys_get_temp_dir() : DIRECTORY_SEPARATOR . 'tmp'; + $this->path = $tempdir . DIRECTORY_SEPARATOR . 'vufind_sessions'; + } + + // Die if the session directory does not exist and cannot be created. + if (!file_exists($this->path) || !is_dir($this->path)) { + if (!@mkdir($this->path)) { + throw new \Exception( + "Cannot access session save path: " . $this->path + ); + } + } + + // Call standard session initialization from this point. + parent::__construct($config); + } + + /** + * Read function must return string value always to make save handler work as + * expected. Return empty string if there is no data to read. + * + * @param string $sess_id The session ID to read + * + * @return string + */ + public function read($sess_id) + { + $sess_file = $this->path . '/sess_' . $sess_id; + if (!file_exists($sess_file)) { + return ''; + } + + // enforce lifetime of this session data + if (filemtime($sess_file) + $this->lifetime <= time()) { + $this->destroy($sess_id); + return ''; + } + + return (string)@file_get_contents($sess_file); + } + + /** + * Write function that is called when session data is to be saved. + * + * @param string $sess_id The current session ID + * @param string $data The session data to write + * + * @return void + */ + public function write($sess_id, $data) + { + $sess_file = $this->path . '/sess_' . $sess_id; + if ($fp = @fopen($sess_file, "w")) { + $return = fwrite($fp, $data); + fclose($fp); + if ($return) { + return; + } + } + // If we got this far, something went wrong with the file output... + // It is tempting to throw an exception here, but this code is called + // outside of the context of exception handling, so all we can do is + // echo a message. + echo 'Cannot write session to ' . $sess_file . "\n"; + } + + /** + * The destroy handler, this is executed when a session is destroyed with + * session_destroy() and takes the session id as its only parameter. + * + * @param string $sess_id The session ID to destroy + * + * @return void + */ + public function destroy($sess_id) + { + // Perform standard actions required by all session methods: + parent::destroy($sess_id); + + // Perform file-specific cleanup: + $sess_file = $this->path . '/sess_' . $sess_id; + return(@unlink($sess_file)); + } + + /** + * The garbage collector, this is executed when the session garbage collector + * is executed and takes the max session lifetime as its only parameter. + * + * @param int $maxlifetime Maximum session lifetime. + * + * @return void + */ + public function gc($maxlifetime) + { + foreach (glob($this->path . "/sess_*") as $filename) { + if (filemtime($filename) + $maxlifetime < time()) { + @unlink($filename); + } + } + return true; + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Session/Memcache.php b/module/VuFind/src/VuFind/Session/Memcache.php new file mode 100644 index 00000000000..1dde4673270 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/Memcache.php @@ -0,0 +1,116 @@ +<?php +/** + * MemCache session handler + * + * Note: This relies on PHP's Memcache extension + * (see http://us.php.net/manual/en/book.memcache.php) + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +namespace VuFind\Session; + +/** + * Memcache session handler + * + * @category VuFind2 + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/creating_a_session_handler Wiki + */ +class Memcache extends AbstractBase +{ + protected $connection; + + /** + * Constructor. + * + * @param Zend_Config $config Session configuration ([Session] section of + * config.ini) + */ + public function __construct($config) + { + // Set defaults if nothing set in config file. + $host = isset($config->memcache_host) ? $config->memcache_host : 'localhost'; + $port = isset($config->memcache_port) ? $config->memcache_port : 11211; + $timeout = isset($config->memcache_connection_timeout) + ? $config->memcache_connection_timeout : 1; + + // Connect to Memcache: + $this->connection = new \Memcache(); + if (!@$this->connection->connect($host, $port, $timeout)) { + throw new \Exception( + "Could not connect to Memcache (host = {$host}, port = {$port})." + ); + } + + // Call standard session initialization from this point. + parent::__construct($config); + } + + /** + * Read function must return string value always to make save handler work as + * expected. Return empty string if there is no data to read. + * + * @param string $sess_id The session ID to read + * + * @return string + */ + public function read($sess_id) + { + return $this->connection->get("vufind_sessions/{$sess_id}"); + } + + /** + * Write function that is called when session data is to be saved. + * + * @param string $sess_id The current session ID + * @param string $data The session data to write + * + * @return void + */ + public function write($sess_id, $data) + { + return $this->connection->set( + "vufind_sessions/{$sess_id}", $data, 0, $this->lifetime + ); + } + + /** + * The destroy handler, this is executed when a session is destroyed with + * session_destroy() and takes the session id as its only parameter. + * + * @param string $sess_id The session ID to destroy + * + * @return void + */ + public function destroy($sess_id) + { + // Perform standard actions required by all session methods: + parent::destroy($sess_id); + + // Perform Memcache-specific cleanup: + return $this->connection->delete("vufind_sessions/{$sess_id}"); + } +} \ No newline at end of file -- GitLab