_config['save_path'])) { log_message('error', 'Session: No Memcached save path configured.'); } if ($this->_config['match_ip'] === TRUE) { $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; } } // ------------------------------------------------------------------------ public function open($save_path, $name) { $this->_memcached = new Memcached(); $server_list = array(); foreach ($this->_memcached->getServerList() as $server) { $server_list[] = $server['host'].':'.$server['port']; } if ( ! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->_config['save_path'], $matches, PREG_SET_ORDER)) { $this->_memcached = NULL; log_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']); return FALSE; } foreach ($matches as $match) { // If Memcached already has this server (or if the port is invalid), skip it if (in_array($match[1].':'.$match[2], $server_list, TRUE)) { log_message('debug', 'Session: Memcached server pool already has '.$match[1].':'.$match[2]); continue; } if ( ! $this->_memcached->addServer($match[1], $match[2], isset($match[3]) ? $match[3] : 0)) { log_message('error', 'Could not add '.$match[1].':'.$match[2].' to Memcached server pool.'); } else { $server_list[] = $server['host'].':'.$server['port']; } } if (empty($server_list)) { log_message('error', 'Session: Memcached server pool is empty.'); return FALSE; } return TRUE; } // ------------------------------------------------------------------------ public function read($session_id) { if (isset($this->_memcached) && $this->_get_lock($session_id)) { // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; $session_data = (string) $this->_memcached->get($this->_key_prefix.$session_id); $this->_fingerprint = md5($session_data); return $session_data; } return FALSE; } public function write($session_id, $session_data) { if ( ! isset($this->_memcached)) { return FALSE; } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { return FALSE; } $this->_fingerprint = md5(''); $this->_session_id = $session_id; } if (isset($this->_lock_key)) { $this->_memcached->replace($this->_lock_key, time(), 5); if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) { $this->_fingerprint = $fingerprint; return TRUE; } return FALSE; } return $this->_memcached->touch($this->_key_prefix.$session_id, $this->_config['expiration']); } return FALSE; } // ------------------------------------------------------------------------ public function close() { if (isset($this->_memcached)) { isset($this->_lock_key) && $this->_memcached->delete($this->_lock_key); if ( ! $this->_memcached->quit()) { return FALSE; } $this->_memcached = NULL; return TRUE; } return FALSE; } // ------------------------------------------------------------------------ public function destroy($session_id) { if (isset($this->_memcached, $this->_lock_key)) { $this->_memcached->delete($this->_key_prefix.$session_id); return $this->_cookie_destroy(); } return FALSE; } // ------------------------------------------------------------------------ public function gc($maxlifetime) { // Not necessary, Memcached takes care of that. return TRUE; } // ------------------------------------------------------------------------ protected function _get_lock($session_id) { if (isset($this->_lock_key)) { return $this->_memcached->replace($this->_lock_key, time(), 5); } $lock_key = $this->_key_prefix.$session_id.':lock'; if ( ! ($ts = $this->_memcached->get($lock_key))) { if ( ! $this->_memcached->set($lock_key, TRUE, 5)) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = $lock_key; $this->_lock = TRUE; return TRUE; } // Another process has the lock, we'll try to wait for it to free itself ... $attempt = 0; while ($attempt++ < 5) { usleep(((time() - $ts) * 1000000) - 20000); if (($ts = $this->_memcached->get($lock_key)) < time()) { continue; } if ( ! $this->_memcached->set($lock_key, time(), 5)) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = $lock_key; break; } if ($attempt === 5) { log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.'); return FALSE; } $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ protected function _release_lock() { if (isset($this->_memcached, $this->_lock_key) && $this->_lock) { if ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND) { log_message('error', 'Session: Error while trying to free lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = NULL; $this->_lock = FALSE; } return TRUE; } } /* End of file Session_memcached_driver.php */ /* Location: ./system/libraries/Session/drivers/Session_memcached_driver.php */