. /** * Memcached based session handler. * * @package core * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core\session; defined('MOODLE_INTERNAL') || die(); /** * Memcached based session handler. * * @package core * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class memcached extends handler { /** @var string $savepath save_path string */ protected $savepath; /** @var array $servers list of servers parsed from save_path */ protected $servers; /** @var string $prefix session key prefix */ protected $prefix; /** * Create new instance of handler. */ public function __construct() { global $CFG; if (empty($CFG->session_memcached_save_path)) { $this->savepath = ''; } else { $this->savepath = $CFG->session_memcached_save_path; } if (empty($this->savepath)) { $this->servers = array(); } else { $this->servers = self::connection_string_to_servers($this->savepath); } if (empty($CFG->session_memcached_prefix)) { $this->prefix = ini_get('memcached.sess_prefix'); } else { $this->prefix = $CFG->session_memcached_prefix; } } /** * Init session handler. */ public function init() { if (!extension_loaded('memcached')) { throw new exception('sessionhandlerproblem', 'error', '', null, 'memcached extension is not loaded'); } $version = phpversion('memcached'); if (!$version or version_compare($version, '2.0') < 0) { throw new exception('sessionhandlerproblem', 'error', '', null, 'memcached extension version must be at least 2.0'); } if (empty($this->savepath)) { throw new exception('sessionhandlerproblem', 'error', '', null, '$CFG->session_memcached_save_path must be specified in config.php'); } // NOTE: we cannot set any lock acquiring timeout here - bad luck. ini_set('session.save_handler', 'memcached'); ini_set('session.save_path', $this->savepath); ini_set('memcached.sess_prefix', $this->prefix); ini_set('memcached.sess_locking', '1'); // Locking is required! } /** * Check for existing session with id $sid. * * Note: this verifies the storage backend only, not the actual session records. * * @param string $sid * @return bool true if session found. */ public function session_exists($sid) { if (!$this->servers) { return false; } $memcached = new \Memcached(); $memcached->addServers($this->servers); $value = $memcached->get($this->prefix.$sid); $memcached->quit(); return ($value !== false); } /** * Kill all active sessions, the core sessions table is * purged afterwards. */ public function kill_all_sessions() { global $DB; if (!$this->servers) { return; } $memcached = new \Memcached(); $memcached->addServers($this->servers); // Note: this can be significantly improved by fetching keys from memcached, // but we need to make sure we are not deleting somebody else's sessions. $rs = $DB->get_recordset('sessions', array(), 'id DESC', 'id, sid'); foreach ($rs as $record) { $memcached->delete($this->prefix.$record->sid); } $rs->close(); $memcached->quit(); } /** * Kill one session, the session record is removed afterwards. * @param string $sid */ public function kill_session($sid) { if (!$this->servers) { return; } $memcached = new \Memcached(); $memcached->addServers($this->servers); $memcached->delete($this->prefix.$sid); $memcached->quit(); } /** * Convert a connection string to an array of servers * * EG: Converts: "abc:123, xyz:789" to * * array( * array('abc', '123'), * array('xyz', '789'), * ) * * @copyright 2013 Moodlerooms Inc. (http://www.moodlerooms.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @author Mark Nielsen * * @param string $str save_path value containing memcached connection string * @return array */ protected static function connection_string_to_servers($str) { $servers = array(); $parts = explode(',', $str); foreach ($parts as $part) { $part = trim($part); $pos = strrpos($part, ':'); if ($pos !== false) { $host = substr($part, 0, $pos); $port = substr($part, ($pos + 1)); } else { $host = $part; $port = 11211; } $servers[] = array($host, $port); } return $servers; } }