Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK!;mSessionHandlerInterface.phpnu[_config =& $params; if (is_php('7')) { $this->_success = TRUE; $this->_failure = FALSE; } else { $this->_success = 0; $this->_failure = -1; } } // ------------------------------------------------------------------------ /** * PHP 5.x validate ID * * Enforces session.use_strict_mode * * @return void */ public function php5_validate_id() { if (isset($_COOKIE[$this->_config['cookie_name']]) && ! $this->validateSessionId($_COOKIE[$this->_config['cookie_name']])) { unset($_COOKIE[$this->_config['cookie_name']]); } } // ------------------------------------------------------------------------ /** * Cookie destroy * * Internal method to force removal of a cookie by the client * when session_destroy() is called. * * @return bool */ protected function _cookie_destroy() { return setcookie( $this->_config['cookie_name'], NULL, 1, $this->_config['cookie_path'], $this->_config['cookie_domain'], $this->_config['cookie_secure'], TRUE ); } // ------------------------------------------------------------------------ /** * Get lock * * A dummy method allowing drivers with no locking functionality * (databases other than PostgreSQL and MySQL) to act as if they * do acquire a lock. * * @param string $session_id * @return bool */ protected function _get_lock($session_id) { $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ /** * Release lock * * @return bool */ protected function _release_lock() { if ($this->_lock) { $this->_lock = FALSE; } return TRUE; } } PK!lDM''$drivers/Session_memcached_driver.phpnu[_config['save_path'])) { log_message('error', 'Session: No Memcached save path configured.'); } if ($this->_config['match_ip'] === TRUE) { $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; } } // ------------------------------------------------------------------------ /** * Open * * Sanitizes save_path and initializes connections. * * @param string $save_path Server path(s) * @param string $name Session cookie name, unused * @return bool */ public function open($save_path, $name) { $this->_memcached = new Memcached(); $this->_memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, TRUE); // required for touch() usage $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 $this->_failure; } 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[] = $match[1].':'.$match[2]; } } if (empty($server_list)) { log_message('error', 'Session: Memcached server pool is empty.'); return $this->_failure; } $this->php5_validate_id(); return $this->_success; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ 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 $this->_failure; } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { if ( ! isset($this->_memcached, $this->_lock_key)) { return $this->_failure; } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { return $this->_failure; } $this->_fingerprint = md5(''); $this->_session_id = $session_id; } $key = $this->_key_prefix.$session_id; $this->_memcached->replace($this->_lock_key, time(), 300); if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { if ($this->_memcached->set($key, $session_data, $this->_config['expiration'])) { $this->_fingerprint = $fingerprint; return $this->_success; } return $this->_failure; } elseif ( $this->_memcached->touch($key, $this->_config['expiration']) OR ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND && $this->_memcached->set($key, $session_data, $this->_config['expiration'])) ) { return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Close * * Releases locks and closes connection. * * @return bool */ public function close() { if (isset($this->_memcached)) { $this->_release_lock(); if ( ! $this->_memcached->quit()) { return $this->_failure; } $this->_memcached = NULL; return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if (isset($this->_memcached, $this->_lock_key)) { $this->_memcached->delete($this->_key_prefix.$session_id); $this->_cookie_destroy(); return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { // Not necessary, Memcached takes care of that. return $this->_success; } // -------------------------------------------------------------------- /** * Validate ID * * Checks whether a session ID record exists server-side, * to enforce session.use_strict_mode. * * @param string $id * @return bool */ public function validateSessionId($id) { $this->_memcached->get($this->_key_prefix.$id); return ($this->_memcached->getResultCode() === Memcached::RES_SUCCESS); } // ------------------------------------------------------------------------ /** * Get lock * * Acquires an (emulated) lock. * * @param string $session_id Session ID * @return bool */ protected function _get_lock($session_id) { // PHP 7 reuses the SessionHandler object on regeneration, // so we need to check here if the lock key is for the // correct session ID. if ($this->_lock_key === $this->_key_prefix.$session_id.':lock') { if ( ! $this->_memcached->replace($this->_lock_key, time(), 300)) { return ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND) ? $this->_memcached->add($this->_lock_key, time(), 300) : FALSE; } return TRUE; } // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; $attempt = 0; do { if ($this->_memcached->get($lock_key)) { sleep(1); continue; } $method = ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND) ? 'add' : 'set'; if ( ! $this->_memcached->$method($lock_key, time(), 300)) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = $lock_key; break; } while (++$attempt < 30); if ($attempt === 30) { log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ /** * Release lock * * Releases a previously acquired lock * * @return bool */ 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->_lock_key); return FALSE; } $this->_lock_key = NULL; $this->_lock = FALSE; } return TRUE; } } PK!_config['save_path'])) { $this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\'); ini_set('session.save_path', $this->_config['save_path']); } else { log_message('debug', 'Session: "sess_save_path" is empty; using "session.save_path" value from php.ini.'); $this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\'); } $this->_sid_regexp = $this->_config['_sid_regexp']; isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload')); } // ------------------------------------------------------------------------ /** * Open * * Sanitizes the save_path directory. * * @param string $save_path Path to session files' directory * @param string $name Session cookie name * @return bool */ public function open($save_path, $name) { if ( ! is_dir($save_path)) { if ( ! mkdir($save_path, 0700, TRUE)) { log_message('error', "Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created."); return $this->_failure; } } elseif ( ! is_writable($save_path)) { log_message('error', "Session: Configured save path '".$this->_config['save_path']."' is not writable by the PHP process."); return $this->_failure; } $this->_config['save_path'] = $save_path; $this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR .$name // we'll use the session cookie name as a prefix to avoid collisions .($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : ''); $this->php5_validate_id(); return $this->_success; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { // This might seem weird, but PHP 5.6 introduces session_reset(), // which re-reads session data if ($this->_file_handle === NULL) { $this->_file_new = ! file_exists($this->_file_path.$session_id); if (($this->_file_handle = fopen($this->_file_path.$session_id, 'c+b')) === FALSE) { log_message('error', "Session: Unable to open file '".$this->_file_path.$session_id."'."); return $this->_failure; } if (flock($this->_file_handle, LOCK_EX) === FALSE) { log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'."); fclose($this->_file_handle); $this->_file_handle = NULL; return $this->_failure; } // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; if ($this->_file_new) { chmod($this->_file_path.$session_id, 0600); $this->_fingerprint = md5(''); return ''; } } // We shouldn't need this, but apparently we do ... // See https://github.com/bcit-ci/CodeIgniter/issues/4039 elseif ($this->_file_handle === FALSE) { return $this->_failure; } else { rewind($this->_file_handle); } $session_data = ''; for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += self::strlen($buffer)) { if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE) { break; } $session_data .= $buffer; } $this->_fingerprint = md5($session_data); return $session_data; } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { // If the two IDs don't match, we have a session_regenerate_id() call // and we need to close the old handle and open a new one if ($session_id !== $this->_session_id && ($this->close() === $this->_failure OR $this->read($session_id) === $this->_failure)) { return $this->_failure; } if ( ! is_resource($this->_file_handle)) { return $this->_failure; } elseif ($this->_fingerprint === md5($session_data)) { return ( ! $this->_file_new && ! touch($this->_file_path.$session_id)) ? $this->_failure : $this->_success; } if ( ! $this->_file_new) { ftruncate($this->_file_handle, 0); rewind($this->_file_handle); } if (($length = strlen($session_data)) > 0) { for ($written = 0; $written < $length; $written += $result) { if (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE) { break; } } if ( ! is_int($result)) { $this->_fingerprint = md5(substr($session_data, 0, $written)); log_message('error', 'Session: Unable to write data.'); return $this->_failure; } } $this->_fingerprint = md5($session_data); return $this->_success; } // ------------------------------------------------------------------------ /** * Close * * Releases locks and closes file descriptor. * * @return bool */ public function close() { if (is_resource($this->_file_handle)) { flock($this->_file_handle, LOCK_UN); fclose($this->_file_handle); $this->_file_handle = $this->_file_new = $this->_session_id = NULL; } return $this->_success; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if ($this->close() === $this->_success) { if (file_exists($this->_file_path.$session_id)) { $this->_cookie_destroy(); return unlink($this->_file_path.$session_id) ? $this->_success : $this->_failure; } return $this->_success; } elseif ($this->_file_path !== NULL) { clearstatcache(); if (file_exists($this->_file_path.$session_id)) { $this->_cookie_destroy(); return unlink($this->_file_path.$session_id) ? $this->_success : $this->_failure; } return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { if ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE) { log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'."); return $this->_failure; } $ts = time() - $maxlifetime; $pattern = ($this->_config['match_ip'] === TRUE) ? '[0-9a-f]{32}' : ''; $pattern = sprintf( '#\A%s'.$pattern.$this->_sid_regexp.'\z#', preg_quote($this->_config['cookie_name']) ); while (($file = readdir($directory)) !== FALSE) { // If the filename doesn't match this pattern, it's either not a session file or is not ours if ( ! preg_match($pattern, $file) OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file) OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE OR $mtime > $ts) { continue; } unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file); } closedir($directory); return $this->_success; } // -------------------------------------------------------------------- /** * Validate ID * * Checks whether a session ID record exists server-side, * to enforce session.use_strict_mode. * * @param string $id * @return bool */ public function validateSessionId($id) { $result = is_file($this->_file_path.$id); clearstatcache(TRUE, $this->_file_path.$id); return $result; } // -------------------------------------------------------------------- /** * Byte-safe strlen() * * @param string $str * @return int */ protected static function strlen($str) { return (self::$func_overload) ? mb_strlen($str, '8bit') : strlen($str); } } PK!9oU++#drivers/Session_database_driver.phpnu[db) OR $CI->load->database(); $this->_db = $CI->db; if ( ! $this->_db instanceof CI_DB_query_builder) { throw new Exception('Query Builder not enabled for the configured database. Aborting.'); } elseif ($this->_db->pconnect) { throw new Exception('Configured database connection is persistent. Aborting.'); } elseif ($this->_db->cache_on) { throw new Exception('Configured database connection has cache enabled. Aborting.'); } $db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver); if (strpos($db_driver, 'mysql') !== FALSE) { $this->_platform = 'mysql'; } elseif (in_array($db_driver, array('postgre', 'pdo_pgsql'), TRUE)) { $this->_platform = 'postgre'; } // Note: BC work-around for the old 'sess_table_name' setting, should be removed in the future. if ( ! isset($this->_config['save_path']) && ($this->_config['save_path'] = config_item('sess_table_name'))) { log_message('debug', 'Session: "sess_save_path" is empty; using BC fallback to "sess_table_name".'); } } // ------------------------------------------------------------------------ /** * Open * * Initializes the database connection * * @param string $save_path Table name * @param string $name Session cookie name, unused * @return bool */ public function open($save_path, $name) { if (empty($this->_db->conn_id) && ! $this->_db->db_connect()) { return $this->_failure; } $this->php5_validate_id(); return $this->_success; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { if ($this->_get_lock($session_id) === FALSE) { return $this->_failure; } // Prevent previous QB calls from messing with our queries $this->_db->reset_query(); // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; $this->_db ->select('data') ->from($this->_config['save_path']) ->where('id', $session_id); if ($this->_config['match_ip']) { $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } if ( ! ($result = $this->_db->get()) OR ($result = $result->row()) === NULL) { // PHP7 will reuse the same SessionHandler object after // ID regeneration, so we need to explicitly set this to // FALSE instead of relying on the default ... $this->_row_exists = FALSE; $this->_fingerprint = md5(''); return ''; } // PostgreSQL's variant of a BLOB datatype is Bytea, which is a // PITA to work with, so we use base64-encoded data in a TEXT // field instead. $result = ($this->_platform === 'postgre') ? base64_decode(rtrim($result->data)) : $result->data; $this->_fingerprint = md5($result); $this->_row_exists = TRUE; return $result; } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { // Prevent previous QB calls from messing with our queries $this->_db->reset_query(); // Was the ID regenerated? if (isset($this->_session_id) && $session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { return $this->_failure; } $this->_row_exists = FALSE; $this->_session_id = $session_id; } elseif ($this->_lock === FALSE) { return $this->_failure; } if ($this->_row_exists === FALSE) { $insert_data = array( 'id' => $session_id, 'ip_address' => $_SERVER['REMOTE_ADDR'], 'timestamp' => time(), 'data' => ($this->_platform === 'postgre' ? base64_encode($session_data) : $session_data) ); if ($this->_db->insert($this->_config['save_path'], $insert_data)) { $this->_fingerprint = md5($session_data); $this->_row_exists = TRUE; return $this->_success; } return $this->_failure; } $this->_db->where('id', $session_id); if ($this->_config['match_ip']) { $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } $update_data = array('timestamp' => time()); if ($this->_fingerprint !== md5($session_data)) { $update_data['data'] = ($this->_platform === 'postgre') ? base64_encode($session_data) : $session_data; } if ($this->_db->update($this->_config['save_path'], $update_data)) { $this->_fingerprint = md5($session_data); return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Close * * Releases locks * * @return bool */ public function close() { return ($this->_lock && ! $this->_release_lock()) ? $this->_failure : $this->_success; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if ($this->_lock) { // Prevent previous QB calls from messing with our queries $this->_db->reset_query(); $this->_db->where('id', $session_id); if ($this->_config['match_ip']) { $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } if ( ! $this->_db->delete($this->_config['save_path'])) { return $this->_failure; } } if ($this->close() === $this->_success) { $this->_cookie_destroy(); return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { // Prevent previous QB calls from messing with our queries $this->_db->reset_query(); return ($this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime))) ? $this->_success : $this->_failure; } // -------------------------------------------------------------------- /** * Validate ID * * Checks whether a session ID record exists server-side, * to enforce session.use_strict_mode. * * @param string $id * @return bool */ public function validateSessionId($id) { // Prevent previous QB calls from messing with our queries $this->_db->reset_query(); $this->_db->select('1')->from($this->_config['save_path'])->where('id', $id); empty($this->_config['match_ip']) OR $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); $result = $this->_db->get(); empty($result) OR $result = $result->row(); return ! empty($result); } // ------------------------------------------------------------------------ /** * Get lock * * Acquires a lock, depending on the underlying platform. * * @param string $session_id Session ID * @return bool */ protected function _get_lock($session_id) { if ($this->_platform === 'mysql') { $arg = md5($session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : '')); if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock) { $this->_lock = $arg; return TRUE; } return FALSE; } elseif ($this->_platform === 'postgre') { $arg = "hashtext('".$session_id."')".($this->_config['match_ip'] ? ", hashtext('".$_SERVER['REMOTE_ADDR']."')" : ''); if ($this->_db->simple_query('SELECT pg_advisory_lock('.$arg.')')) { $this->_lock = $arg; return TRUE; } return FALSE; } return parent::_get_lock($session_id); } // ------------------------------------------------------------------------ /** * Release lock * * Releases a previously acquired lock * * @return bool */ protected function _release_lock() { if ( ! $this->_lock) { return TRUE; } if ($this->_platform === 'mysql') { if ($this->_db->query("SELECT RELEASE_LOCK('".$this->_lock."') AS ci_session_lock")->row()->ci_session_lock) { $this->_lock = FALSE; return TRUE; } return FALSE; } elseif ($this->_platform === 'postgre') { if ($this->_db->simple_query('SELECT pg_advisory_unlock('.$this->_lock.')')) { $this->_lock = FALSE; return TRUE; } return FALSE; } return parent::_release_lock(); } } PK!$<.<. drivers/Session_redis_driver.phpnu[=')) { $this->_setTimeout_name = 'expire'; $this->_delete_name = 'del'; $this->_ping_success = TRUE; } else { $this->_setTimeout_name = 'setTimeout'; $this->_delete_name = 'delete'; $this->_ping_success = '+PONG'; } if (empty($this->_config['save_path'])) { log_message('error', 'Session: No Redis save path configured.'); } elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->_config['save_path'], $matches)) { isset($matches[3]) OR $matches[3] = ''; // Just to avoid undefined index notices below $this->_config['save_path'] = array( 'host' => $matches[1], 'port' => empty($matches[2]) ? NULL : $matches[2], 'password' => preg_match('#auth=([^\s&]+)#', $matches[3], $match) ? $match[1] : NULL, 'database' => preg_match('#database=(\d+)#', $matches[3], $match) ? (int) $match[1] : NULL, 'timeout' => preg_match('#timeout=(\d+\.\d+)#', $matches[3], $match) ? (float) $match[1] : NULL ); preg_match('#prefix=([^\s&]+)#', $matches[3], $match) && $this->_key_prefix = $match[1]; } else { log_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']); } if ($this->_config['match_ip'] === TRUE) { $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; } } // ------------------------------------------------------------------------ /** * Open * * Sanitizes save_path and initializes connection. * * @param string $save_path Server path * @param string $name Session cookie name, unused * @return bool */ public function open($save_path, $name) { if (empty($this->_config['save_path'])) { return $this->_failure; } $redis = new Redis(); if ( ! $redis->connect($this->_config['save_path']['host'], $this->_config['save_path']['port'], $this->_config['save_path']['timeout'])) { log_message('error', 'Session: Unable to connect to Redis with the configured settings.'); } elseif (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password'])) { log_message('error', 'Session: Unable to authenticate to Redis instance.'); } elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database'])) { log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']); } else { $this->_redis = $redis; $this->php5_validate_id(); return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { if (isset($this->_redis) && $this->_get_lock($session_id)) { // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; $session_data = $this->_redis->get($this->_key_prefix.$session_id); is_string($session_data) ? $this->_key_exists = TRUE : $session_data = ''; $this->_fingerprint = md5($session_data); return $session_data; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { if ( ! isset($this->_redis, $this->_lock_key)) { return $this->_failure; } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { return $this->_failure; } $this->_key_exists = FALSE; $this->_session_id = $session_id; } $this->_redis->{$this->_setTimeout_name}($this->_lock_key, 300); if ($this->_fingerprint !== ($fingerprint = md5($session_data)) OR $this->_key_exists === FALSE) { if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) { $this->_fingerprint = $fingerprint; $this->_key_exists = TRUE; return $this->_success; } return $this->_failure; } return ($this->_redis->{$this->_setTimeout_name}($this->_key_prefix.$session_id, $this->_config['expiration'])) ? $this->_success : $this->_failure; } // ------------------------------------------------------------------------ /** * Close * * Releases locks and closes connection. * * @return bool */ public function close() { if (isset($this->_redis)) { try { if ($this->_redis->ping() === $this->_ping_success) { $this->_release_lock(); if ($this->_redis->close() === FALSE) { return $this->_failure; } } } catch (RedisException $e) { log_message('error', 'Session: Got RedisException on close(): '.$e->getMessage()); } $this->_redis = NULL; return $this->_success; } return $this->_success; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if (isset($this->_redis, $this->_lock_key)) { if (($result = $this->_redis->{$this->_delete_name}($this->_key_prefix.$session_id)) !== 1) { log_message('debug', 'Session: Redis::'.$this->_delete_name.'() expected to return 1, got '.var_export($result, TRUE).' instead.'); } $this->_cookie_destroy(); return $this->_success; } return $this->_failure; } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { // Not necessary, Redis takes care of that. return $this->_success; } // -------------------------------------------------------------------- /** * Validate ID * * Checks whether a session ID record exists server-side, * to enforce session.use_strict_mode. * * @param string $id * @return bool */ public function validateSessionId($id) { return (bool) $this->_redis->exists($this->_key_prefix.$id); } // ------------------------------------------------------------------------ /** * Get lock * * Acquires an (emulated) lock. * * @param string $session_id Session ID * @return bool */ protected function _get_lock($session_id) { // PHP 7 reuses the SessionHandler object on regeneration, // so we need to check here if the lock key is for the // correct session ID. if ($this->_lock_key === $this->_key_prefix.$session_id.':lock') { return $this->_redis->{$this->_setTimeout_name}($this->_lock_key, 300); } // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; $attempt = 0; do { if (($ttl = $this->_redis->ttl($lock_key)) > 0) { sleep(1); continue; } if ($ttl === -2 && ! $this->_redis->set($lock_key, time(), array('nx', 'ex' => 300))) { // Sleep for 1s to wait for lock releases. sleep(1); continue; } elseif ( ! $this->_redis->setex($lock_key, 300, time())) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = $lock_key; break; } while (++$attempt < 30); if ($attempt === 30) { log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } elseif ($ttl === -1) { log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); } $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ /** * Release lock * * Releases a previously acquired lock * * @return bool */ protected function _release_lock() { if (isset($this->_redis, $this->_lock_key) && $this->_lock) { if ( ! $this->_redis->{$this->_delete_name}($this->_lock_key)) { log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } $this->_lock_key = NULL; $this->_lock = FALSE; } return TRUE; } } PK!(drivers/index.htmlnu[ 403 Forbidden

Directory access is forbidden.

PK!e6 Session.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; // Help opcache.preload discover always-needed symbols class_exists(AttributeBag::class); class_exists(FlashBag::class); class_exists(SessionBagProxy::class); /** * @author Fabien Potencier * @author Drak */ class Session implements SessionInterface, \IteratorAggregate, \Countable { protected $storage; private $flashName; private $attributeName; private $data = []; private $usageIndex = 0; public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) { $this->storage = $storage ?: new NativeSessionStorage(); $attributes = $attributes ?: new AttributeBag(); $this->attributeName = $attributes->getName(); $this->registerBag($attributes); $flashes = $flashes ?: new FlashBag(); $this->flashName = $flashes->getName(); $this->registerBag($flashes); } /** * {@inheritdoc} */ public function start() { return $this->storage->start(); } /** * {@inheritdoc} */ public function has($name) { return $this->getAttributeBag()->has($name); } /** * {@inheritdoc} */ public function get($name, $default = null) { return $this->getAttributeBag()->get($name, $default); } /** * {@inheritdoc} */ public function set($name, $value) { $this->getAttributeBag()->set($name, $value); } /** * {@inheritdoc} */ public function all() { return $this->getAttributeBag()->all(); } /** * {@inheritdoc} */ public function replace(array $attributes) { $this->getAttributeBag()->replace($attributes); } /** * {@inheritdoc} */ public function remove($name) { return $this->getAttributeBag()->remove($name); } /** * {@inheritdoc} */ public function clear() { $this->getAttributeBag()->clear(); } /** * {@inheritdoc} */ public function isStarted() { return $this->storage->isStarted(); } /** * Returns an iterator for attributes. * * @return \ArrayIterator An \ArrayIterator instance */ public function getIterator() { return new \ArrayIterator($this->getAttributeBag()->all()); } /** * Returns the number of attributes. * * @return int */ public function count() { return \count($this->getAttributeBag()->all()); } public function &getUsageIndex(): int { return $this->usageIndex; } /** * @internal */ public function isEmpty(): bool { if ($this->isStarted()) { ++$this->usageIndex; } foreach ($this->data as &$data) { if (!empty($data)) { return false; } } return true; } /** * {@inheritdoc} */ public function invalidate($lifetime = null) { $this->storage->clear(); return $this->migrate(true, $lifetime); } /** * {@inheritdoc} */ public function migrate($destroy = false, $lifetime = null) { return $this->storage->regenerate($destroy, $lifetime); } /** * {@inheritdoc} */ public function save() { $this->storage->save(); } /** * {@inheritdoc} */ public function getId() { return $this->storage->getId(); } /** * {@inheritdoc} */ public function setId($id) { if ($this->storage->getId() !== $id) { $this->storage->setId($id); } } /** * {@inheritdoc} */ public function getName() { return $this->storage->getName(); } /** * {@inheritdoc} */ public function setName($name) { $this->storage->setName($name); } /** * {@inheritdoc} */ public function getMetadataBag() { ++$this->usageIndex; return $this->storage->getMetadataBag(); } /** * {@inheritdoc} */ public function registerBag(SessionBagInterface $bag) { $this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex)); } /** * {@inheritdoc} */ public function getBag($name) { $bag = $this->storage->getBag($name); return method_exists($bag, 'getBag') ? $bag->getBag() : $bag; } /** * Gets the flashbag interface. * * @return FlashBagInterface */ public function getFlashBag() { return $this->getBag($this->flashName); } /** * Gets the attributebag interface. * * Note that this method was added to help with IDE autocompletion. */ private function getAttributeBag(): AttributeBagInterface { return $this->getBag($this->attributeName); } } PK!( index.htmlnu[ 403 Forbidden

Directory access is forbidden.

PK!ݛ{ Cookie/unpacked_cookie_data-i.rinu[U:RDoc::AnyMethod[iI"unpacked_cookie_data:EFI"/Rack::Session::Cookie#unpacked_cookie_data;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I" (env);T@ FI" Cookie;TcRDoc::NormalClass00PK!,,$Cookie/persistent_session_id%21-i.rinu[U:RDoc::AnyMethod[iI"persistent_session_id!:EFI"1Rack::Session::Cookie#persistent_session_id!;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I"(data, sid=nil);T@ FI" Cookie;TcRDoc::NormalClass00PK!^ӺCookie/Identity/decode-i.rinu[U:RDoc::AnyMethod[iI" decode:EFI"+Rack::Session::Cookie::Identity#decode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!֬>!Cookie/Identity/cdesc-Identity.rinu[U:RDoc::NormalClass[iI" Identity:EFI"$Rack::Session::Cookie::Identity;FI" Object;Fo:RDoc::Markup::Document: @parts[o;;[o:RDoc::Markup::Paragraph;[I"(Use no encoding for session cookies;F: @fileI"lib/rack/session/cookie.rb;F; 0[[[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[I" decode;F@[I" encode;F@[; [[; [PK!WĹCookie/Identity/encode-i.rinu[U:RDoc::AnyMethod[iI" encode:EFI"+Rack::Session::Cookie::Identity#encode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!|%>Cookie/new-c.rinu[U:RDoc::AnyMethod[iI"new:EFI"Rack::Session::Cookie::new;FT: publico:RDoc::Markup::Document: @parts[: @file000[I"(app, options={});FI"lib/rack/session/cookie.rb;FPK!\Cookie/cdesc-Cookie.rinu[U:RDoc::NormalClass[iI" Cookie:EFI"Rack::Session::Cookie;FI"Abstract::ID;Fo:RDoc::Markup::Document: @parts[o;;[o:RDoc::Markup::Paragraph;[ I"KRack::Session::Cookie provides simple cookie based session management.;FI"OBy default, the session is a Ruby Hash stored as base64 encoded marshalled;FI"Kdata set to :key (default: rack.session). The object that encodes the;FI"Lsession data is configurable and must respond to +encode+ and +decode+.;FI"9Both methods must take a string and return a string.;Fo:RDoc::Markup::BlankLineo; ;[I"KWhen the secret key is set, cookie data is checked for data integrity.;FI"MThe old secret key is also accepted and allows graceful secret rotation.;F@o; ;[I" Example:;F@o:RDoc::Markup::Verbatim;[ I"8use Rack::Session::Cookie, :key => 'rack.session', ;FI"6 :domain => 'foo.com', ;FI". :path => '/', ;FI": :expire_after => 2592000, ;FI"8 :secret => 'change_me', ;FI"@ :old_secret => 'also_change_me' ;FI" ;FI""All parameters are optional. ;Fo; ;[I"*Example of a cookie with no encoding:;F@o; ;[I".Rack::Session::Cookie.new(application, { ;FI"5 :coder => Rack::Session::Cookie::Identity.new ;FI"}) ;Fo; ;[I".Example of a cookie with custom encoding:;F@o; ;[ I".Rack::Session::Cookie.new(application, { ;FI" :coder => Class.new { ;FI"+ def encode(str); str.reverse; end ;FI"+ def decode(str); str.reverse; end ;FI" }.new ;FI"});F: @fileI"lib/rack/session/cookie.rb;F; 0[[ I" coder;FI"R;F: publicF@:[[[[I" class;F[[; [[I"new;F@:[:protected[[: private[[I" instance;F[[; [[;[[;[PK!Ԗ**Cookie/set_session-i.rinu[U:RDoc::AnyMethod[iI"set_session:EFI"&Rack::Session::Cookie#set_session;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I"((env, session_id, session, options);T@ FI" Cookie;TcRDoc::NormalClass00PK!XDCookie/digest_match%3f-i.rinu[U:RDoc::AnyMethod[iI"digest_match?:EFI"(Rack::Session::Cookie#digest_match?;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I"(data, digest);T@ FI" Cookie;TcRDoc::NormalClass00PK!{2 Cookie/Base64/JSON/cdesc-JSON.rinu[U:RDoc::NormalClass[iI" JSON:EFI"(Rack::Session::Cookie::Base64::JSON;FI""Rack::Session::Cookie::Base64;Fo:RDoc::Markup::Document: @parts[o;;[o:RDoc::Markup::Paragraph;[I"HN.B. Unlike other encoding methods, the contained objects must be a;FI":valid JSON composite type, either a Hash or an Array.;F: @fileI"lib/rack/session/cookie.rb;F; 0[[[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[I" decode;F@[I" encode;F@[; [[; [PK!M>Cookie/Base64/JSON/decode-i.rinu[U:RDoc::AnyMethod[iI" decode:EFI"/Rack::Session::Cookie::Base64::JSON#decode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!Cookie/Base64/JSON/encode-i.rinu[U:RDoc::AnyMethod[iI" encode:EFI"/Rack::Session::Cookie::Base64::JSON#encode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (obj);FI"lib/rack/session/cookie.rb;FPK!}}Cookie/Base64/cdesc-Base64.rinu[U:RDoc::NormalClass[iI" Base64:EFI""Rack::Session::Cookie::Base64;FI" Object;Fo:RDoc::Markup::Document: @parts[o;;[o:RDoc::Markup::Paragraph;[I"%Encode session cookies as Base64;F: @fileI"lib/rack/session/cookie.rb;F; 0[[[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[I" decode;F@[I" encode;F@[; [[; [PK!opaCookie/Base64/decode-i.rinu[U:RDoc::AnyMethod[iI" decode:EFI")Rack::Session::Cookie::Base64#decode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!W!Cookie/Base64/Marshal/decode-i.rinu[U:RDoc::AnyMethod[iI" decode:EFI"2Rack::Session::Cookie::Base64::Marshal#decode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!&.&Cookie/Base64/Marshal/cdesc-Marshal.rinu[U:RDoc::NormalClass[iI" Marshal:EFI"+Rack::Session::Cookie::Base64::Marshal;FI""Rack::Session::Cookie::Base64;Fo:RDoc::Markup::Document: @parts[o;;[o:RDoc::Markup::Paragraph;[I"4Encode session cookies as Marshaled Base64 data;F: @fileI"lib/rack/session/cookie.rb;F; 0[[[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[I" decode;F@[I" encode;F@[; [[; [PK!!Cookie/Base64/Marshal/encode-i.rinu[U:RDoc::AnyMethod[iI" encode:EFI"2Rack::Session::Cookie::Base64::Marshal#encode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!90!Cookie/Base64/ZipJSON/decode-i.rinu[U:RDoc::AnyMethod[iI" decode:EFI"2Rack::Session::Cookie::Base64::ZipJSON#decode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!X4LL&Cookie/Base64/ZipJSON/cdesc-ZipJSON.rinu[U:RDoc::NormalClass[iI" ZipJSON:EFI"+Rack::Session::Cookie::Base64::ZipJSON;FI""Rack::Session::Cookie::Base64;Fo:RDoc::Markup::Document: @parts[: @file0[[[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[I" decode;FI"lib/rack/session/cookie.rb;F[I" encode;F@ [; [[; [PK!!Cookie/Base64/ZipJSON/encode-i.rinu[U:RDoc::AnyMethod[iI" encode:EFI"2Rack::Session::Cookie::Base64::ZipJSON#encode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (obj);FI"lib/rack/session/cookie.rb;FPK!T Cookie/Base64/encode-i.rinu[U:RDoc::AnyMethod[iI" encode:EFI")Rack::Session::Cookie::Base64#encode;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (str);FI"lib/rack/session/cookie.rb;FPK!}ȁCookie/extract_session_id-i.rinu[U:RDoc::AnyMethod[iI"extract_session_id:EFI"-Rack::Session::Cookie#extract_session_id;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I" (env);T@ FI" Cookie;TcRDoc::NormalClass00PK!zCCookie/get_session-i.rinu[U:RDoc::AnyMethod[iI"get_session:EFI"&Rack::Session::Cookie#get_session;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I"(env, sid);T@ FI" Cookie;TcRDoc::NormalClass00PK! |))Cookie/destroy_session-i.rinu[U:RDoc::AnyMethod[iI"destroy_session:EFI"*Rack::Session::Cookie#destroy_session;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I"(env, session_id, options);T@ FI" Cookie;TcRDoc::NormalClass00PK!P+%Cookie/generate_hmac-i.rinu[U:RDoc::AnyMethod[iI"generate_hmac:EFI"(Rack::Session::Cookie#generate_hmac;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"lib/rack/session/cookie.rb;T:0@omit_headings_from_table_of_contents_below000[I"(data, secret);T@ FI" Cookie;TcRDoc::NormalClass00PK!ACookie/coder-i.rinu[U:RDoc::Attr[ iI" coder:EFI" Rack::Session::Cookie#coder;FI"R;F: publico:RDoc::Markup::Document: @parts[: @file0FI"lib/rack/session/cookie.rb;FPK!]|Pool/mutex-i.rinu[U:RDoc::Attr[ iI" mutex:EFI"Rack::Session::Pool#mutex;FI"R;F: publico:RDoc::Markup::Document: @parts[: @file0FI"lib/rack/session/pool.rb;FPK!By Pool/new-c.rinu[U:RDoc::AnyMethod[iI"new:EFI"Rack::Session::Pool::new;FT: publico:RDoc::Markup::Document: @parts[: @file000[I"(app, options={});FI"lib/rack/session/pool.rb;FPK!TtPool/set_session-i.rinu[U:RDoc::AnyMethod[iI"set_session:EFI"$Rack::Session::Pool#set_session;FF: publico:RDoc::Markup::Document: @parts[: @file000[I",(env, session_id, new_session, options);FI"lib/rack/session/pool.rb;FPK!0VPool/with_lock-i.rinu[U:RDoc::AnyMethod[iI"with_lock:EFI""Rack::Session::Pool#with_lock;FF: publico:RDoc::Markup::Document: @parts[: @file00I";F[I" (env);FI"lib/rack/session/pool.rb;FPK!wlPool/pool-i.rinu[U:RDoc::Attr[ iI" pool:EFI"Rack::Session::Pool#pool;FI"R;F: publico:RDoc::Markup::Document: @parts[: @file0FI"lib/rack/session/pool.rb;FPK!uIZPool/cdesc-Pool.rinu[U:RDoc::NormalClass[iI" Pool:EFI"Rack::Session::Pool;FI"Abstract::ID;Fo:RDoc::Markup::Document: @parts[o;;[ o:RDoc::Markup::Paragraph;[ I"IRack::Session::Pool provides simple cookie based session management.;FI"4Session data is stored in a hash held by @pool.;FI"BIn the context of a multithreaded environment, sessions being;FI"7committed to the pool is done in a merging manner.;Fo:RDoc::Markup::BlankLineo; ;[I"IThe :drop option is available in rack.session.options if you wish to;FI":explicitly remove the session from the session cache.;F@o; ;[I" Example:;Fo:RDoc::Markup::Verbatim;[ I"myapp = MyRackApp.new ;FI"0sessioned = Rack::Session::Pool.new(myapp, ;FI" :domain => 'foo.com', ;FI" :expire_after => 2592000 ;FI") ;FI")Rack::Handler::WEBrick.run sessioned;F: @fileI"lib/rack/session/pool.rb;F; 0[[ I" mutex;FI"R;F: publicF@$[ I" pool;F@(; F@$[[I"DEFAULT_OPTIONS;Fo;;[; 0@$[[[I" class;F[[; [[I"new;F@$[:protected[[: private[[I" instance;F[[; [ [I"destroy_session;F@$[I"generate_sid;F@$[I"get_session;F@$[I"set_session;F@$[I"with_lock;F@$[;[[;[PK! |ӴPool/generate_sid-i.rinu[U:RDoc::AnyMethod[iI"generate_sid:EFI"%Rack::Session::Pool#generate_sid;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"lib/rack/session/pool.rb;FPK!侺Pool/get_session-i.rinu[U:RDoc::AnyMethod[iI"get_session:EFI"$Rack::Session::Pool#get_session;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"(env, sid);FI"lib/rack/session/pool.rb;FPK!B9Pool/destroy_session-i.rinu[U:RDoc::AnyMethod[iI"destroy_session:EFI"(Rack::Session::Pool#destroy_session;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"(env, session_id, options);FI"lib/rack/session/pool.rb;FPK!)0cdesc-Session.rinu[U:RDoc::NormalModule[iI" Session:EFI"Rack::Session;F0o:RDoc::Markup::Document: @parts[: @file0[[[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[; [[; [PK!‡9QQAbstract/cdesc-Abstract.rinu[U:RDoc::NormalModule[iI" Abstract:EFI"Rack::Session::Abstract;F0o:RDoc::Markup::Document: @parts[: @file0[[[I"ENV_SESSION_KEY;Fo;;[; 0I"$lib/rack/session/abstract/id.rb;F[I"ENV_SESSION_OPTIONS_KEY;Fo;;[; 0@[[[I" class;F[[: public[[:protected[[: private[[I" instance;F[[; [[; [[; [PK!DB Abstract/SessionHash/delete-i.rinu[U:RDoc::AnyMethod[iI" delete:EFI"0Rack::Session::Abstract::SessionHash#delete;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (key);FI"$lib/rack/session/abstract/id.rb;FPK!rv Abstract/SessionHash/values-i.rinu[U:RDoc::AnyMethod[iI" values:EFI"0Rack::Session::Abstract::SessionHash#values;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!_J_Abstract/SessionHash/clear-i.rinu[U:RDoc::AnyMethod[iI" clear:EFI"/Rack::Session::Abstract::SessionHash#clear;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!`"Abstract/SessionHash/empty%3f-i.rinu[U:RDoc::AnyMethod[iI" empty?:EFI"0Rack::Session::Abstract::SessionHash#empty?;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!'M!Abstract/SessionHash/replace-i.rinu[U:RDoc::AnyMethod[iI" replace:EFI"1Rack::Session::Abstract::SessionHash#replace;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (hash);FI"$lib/rack/session/abstract/id.rb;FPK!jQ$Abstract/SessionHash/has_key%3f-i.rinu[U:RDoc::AnyMethod[iI" has_key?:EFI"2Rack::Session::Abstract::SessionHash#has_key?;FF: publico:RDoc::Markup::Document: @parts[: @file000[[I" key?;Fo;; [; 0[I" include?;Fo;; [; 0I" (key);FI"$lib/rack/session/abstract/id.rb;FPK!Z#Abstract/SessionHash/exists%3f-i.rinu[U:RDoc::AnyMethod[iI" exists?:EFI"1Rack::Session::Abstract::SessionHash#exists?;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!>((*Abstract/SessionHash/load_for_read%21-i.rinu[U:RDoc::AnyMethod[iI"load_for_read!:EFI"8Rack::Session::Abstract::SessionHash#load_for_read!;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"();T@ FI"SessionHash;TcRDoc::NormalClass00PK!QAbstract/SessionHash/new-c.rinu[U:RDoc::AnyMethod[iI"new:EFI".Rack::Session::Abstract::SessionHash::new;FT: publico:RDoc::Markup::Document: @parts[: @file000[I"(store, env);FI"$lib/rack/session/abstract/id.rb;FPK!`e Abstract/SessionHash/key%3f-i.rinu[U:RDoc::AnyMethod[iI" key?:EFI".Rack::Session::Abstract::SessionHash#key?;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (key);FI"$lib/rack/session/abstract/id.rb;FPK!-Abstract/SessionHash/store-i.rinu[U:RDoc::AnyMethod[iI" store:EFI"/Rack::Session::Abstract::SessionHash#store;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"(key, value);FI"$lib/rack/session/abstract/id.rb;FPK!^f$Abstract/SessionHash/include%3f-i.rinu[U:RDoc::AnyMethod[iI" include?:EFI"2Rack::Session::Abstract::SessionHash#include?;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (key);FI"$lib/rack/session/abstract/id.rb;FPK!LAbstract/SessionHash/set-c.rinu[U:RDoc::AnyMethod[iI"set:EFI".Rack::Session::Abstract::SessionHash::set;FT: publico:RDoc::Markup::Document: @parts[: @file000[I"(env, session);FI"$lib/rack/session/abstract/id.rb;FPK!ZHo--(Abstract/SessionHash/stringify_keys-i.rinu[U:RDoc::AnyMethod[iI"stringify_keys:EFI"8Rack::Session::Abstract::SessionHash#stringify_keys;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I" (other);T@ FI"SessionHash;TcRDoc::NormalClass00PK!h!Abstract/SessionHash/destroy-i.rinu[U:RDoc::AnyMethod[iI" destroy:EFI"1Rack::Session::Abstract::SessionHash#destroy;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!Th Abstract/SessionHash/%5b%5d-i.rinu[U:RDoc::AnyMethod[iI"[]:EFI",Rack::Session::Abstract::SessionHash#[];FF: publico:RDoc::Markup::Document: @parts[: @file000[[I" fetch;Fo;; [; 0I" (key);FI"$lib/rack/session/abstract/id.rb;FPK!gAOAbstract/SessionHash/fetch-i.rinu[U:RDoc::AnyMethod[iI" fetch:EFI"/Rack::Session::Abstract::SessionHash#fetch;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (key);FI"$lib/rack/session/abstract/id.rb;FPK!c(Abstract/SessionHash/each-i.rinu[U:RDoc::AnyMethod[iI" each:EFI".Rack::Session::Abstract::SessionHash#each;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (&block);FI"$lib/rack/session/abstract/id.rb;FPK![Abstract/SessionHash/find-c.rinu[U:RDoc::AnyMethod[iI" find:EFI"/Rack::Session::Abstract::SessionHash::find;FT: publico:RDoc::Markup::Document: @parts[: @file000[I" (env);FI"$lib/rack/session/abstract/id.rb;FPK!*5!Abstract/SessionHash/options-i.rinu[U:RDoc::AnyMethod[iI" options:EFI"1Rack::Session::Abstract::SessionHash#options;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!!Abstract/SessionHash/to_hash-i.rinu[U:RDoc::AnyMethod[iI" to_hash:EFI"1Rack::Session::Abstract::SessionHash#to_hash;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!L)Abstract/SessionHash/keys-i.rinu[U:RDoc::AnyMethod[iI" keys:EFI".Rack::Session::Abstract::SessionHash#keys;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"();FI"$lib/rack/session/abstract/id.rb;FPK!ZO"Abstract/SessionHash/merge%21-i.rinu[U:RDoc::AnyMethod[iI" merge!:EFI"0Rack::Session::Abstract::SessionHash#merge!;FF: publico:RDoc::Markup::Document: @parts[: @file000[I" (hash);FI"$lib/rack/session/abstract/id.rb;FPK!Z**+Abstract/SessionHash/load_for_write%21-i.rinu[U:RDoc::AnyMethod[iI"load_for_write!:EFI"9Rack::Session::Abstract::SessionHash#load_for_write!;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"();T@ FI"SessionHash;TcRDoc::NormalClass00PK!#Abstract/SessionHash/%5b%5d%3d-i.rinu[U:RDoc::AnyMethod[iI"[]=:EFI"-Rack::Session::Abstract::SessionHash#[]=;FF: publico:RDoc::Markup::Document: @parts[: @file000[[I" store;Fo;; [; 0I"(key, value);FI"$lib/rack/session/abstract/id.rb;FPK!!Abstract/ID/force_options%3f-i.rinu[U:RDoc::AnyMethod[iI"force_options?:EFI"/Rack::Session::Abstract::ID#force_options?;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(options);T@ FI"ID;TcRDoc::NormalClass00PK!T?Q66)Abstract/ID/forced_session_update%3f-i.rinu[U:RDoc::AnyMethod[iI"forced_session_update?:EFI"7Rack::Session::Abstract::ID#forced_session_update?;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(session, options);T@ FI"ID;TcRDoc::NormalClass00PK!PPAbstract/ID/commit_session-i.rinu[U:RDoc::AnyMethod[iI"commit_session:EFI"/Rack::Session::Abstract::ID#commit_session;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [ I"GAcquires the session from the environment and the session id from ;TI"Hthe session options and passes them to #set_session. If successful ;TI"Fand the :defer option is not true, a cookie will be added to the ;TI"$response with the session's id.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"!(env, status, headers, body);T@FI"ID;TcRDoc::NormalClass00PK!k)z(($Abstract/ID/security_matches%3f-i.rinu[U:RDoc::AnyMethod[iI"security_matches?:EFI"2Rack::Session::Abstract::ID#security_matches?;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(env, options);T@ FI"ID;TcRDoc::NormalClass00PK!D  "Abstract/ID/commit_session%3f-i.rinu[U:RDoc::AnyMethod[iI"commit_session?:EFI"0Rack::Session::Abstract::ID#commit_session?;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"^Session should be committed if it was loaded, any of specific options like :renew, :drop ;TI"[or :expire_after was given and the security permissions match. Skips if skip is given.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(env, session, options);T@FI"ID;TcRDoc::NormalClass00PK!␍0Abstract/ID/new-c.rinu[U:RDoc::AnyMethod[iI"new:EFI"%Rack::Session::Abstract::ID::new;FT: publico:RDoc::Markup::Document: @parts[: @file000[I"(app, options={});FI"$lib/rack/session/abstract/id.rb;FPK!^??Abstract/ID/cdesc-ID.rinu[U:RDoc::NormalClass[iI"ID:EFI" Rack::Session::Abstract::ID;FI" Object;Fo:RDoc::Markup::Document: @parts[o;;[o:RDoc::Markup::Paragraph;[ I"IID sets up a basic framework for implementing an id based sessioning;FI"Kservice. Cookies sent to the client for maintaining sessions will only;FI"Dcontain an id reference. Only #get_session and #set_session are;FI" required to be overwritten.;Fo:RDoc::Markup::BlankLineo; ;[I"!All parameters are optional.;Fo:RDoc::Markup::List: @type: BULLET: @items[ o:RDoc::Markup::ListItem: @label0;[o; ;[I"=:key determines the name of the cookie, by default it is;FI"'rack.session';Fo;;0;[o; ;[I"J:path, :domain, :expire_after, :secure, and :httponly set the related;FI"3cookie options as by Rack::Response#add_cookie;Fo;;0;[o; ;[I"O:skip will not a set a cookie in the response nor update the session state;Fo;;0;[o; ;[I"N:defer will not set a cookie in the response but still update the session;FI"'state if it is used with a backend;Fo;;0;[o; ;[I"J:renew (implementation dependent) will prompt the generation of a new;FI"Isession id, and migration of data to be referenced at the new id. If;FI"E:defer is set, it will be overridden and the cookie will be set.;Fo;;0;[o; ;[I"H:sidbits sets the number of bits in length that a generated session;FI"id will be.;F@o; ;[ I"HThese options can be set on a per request basis, at the location of;FI"Kenv['rack.session.options']. Additionally the id of the session can be;FI"Cfound within the options hash at the key :id. It is highly not;FI"%recommended to change its value.;F@o; ;[I"(Is Rack::Utils::Context compatible.;F@o; ;[I"INot included by default; you must require 'rack/session/abstract/id';FI" to use.;F: @fileI"$lib/rack/session/abstract/id.rb;F;0[[ I"default_options;FI"R;F: publicF@K[ I"key;F@O;F@K[[I"DEFAULT_OPTIONS;Fo;;[;0@K[[[I" class;F[[;[[I"new;F@K[:protected[[: private[[I" instance;F[[;[[I" call;F@K[I" context;F@K[;[[;[PK!.{{#Abstract/ID/current_session_id-i.rinu[U:RDoc::AnyMethod[iI"current_session_id:EFI"3Rack::Session::Abstract::ID#current_session_id;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"9Returns the current session id from the SessionHash.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I" (env);T@FI"ID;TcRDoc::NormalClass00PK!]Abstract/ID/context-i.rinu[U:RDoc::AnyMethod[iI" context:EFI"(Rack::Session::Abstract::ID#context;FF: publico:RDoc::Markup::Document: @parts[: @file000[I"(env, app=@app);FI"$lib/rack/session/abstract/id.rb;FPK!Abstract/ID/set_session-i.rinu[U:RDoc::AnyMethod[iI"set_session:EFI",Rack::Session::Abstract::ID#set_session;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"IAll thread safety and session storage procedures should occur here. ;TI"JMust return the session id if the session was saved successfully, or ;TI"-false if the session could not be saved.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"!(env, sid, session, options);T@FI"ID;TcRDoc::NormalClass00PK!98{{Abstract/ID/session_class-i.rinu[U:RDoc::AnyMethod[iI"session_class:EFI".Rack::Session::Abstract::ID#session_class;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"FAllow subclasses to prepare_session for different Session classes;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"();T@FI"ID;TcRDoc::NormalClass00PK!RUIAbstract/ID/set_cookie-i.rinu[U:RDoc::AnyMethod[iI"set_cookie:EFI"+Rack::Session::Abstract::ID#set_cookie;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"LSets the cookie back to the client with session id. We skip the cookie ;TI"Osetting if the value didn't change (sid is the same) or expires was given.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(env, headers, cookie);T@FI"ID;TcRDoc::NormalClass00PK!0nn#Abstract/ID/extract_session_id-i.rinu[U:RDoc::AnyMethod[iI"extract_session_id:EFI"3Rack::Session::Abstract::ID#extract_session_id;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I",Extract session id from request object.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I" (env);T@FI"ID;TcRDoc::NormalClass00PK!"Abstract/ID/loaded_session%3f-i.rinu[U:RDoc::AnyMethod[iI"loaded_session?:EFI"0Rack::Session::Abstract::ID#loaded_session?;TF: privateo:RDoc::Markup::Document: @parts[: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(session);T@ FI"ID;TcRDoc::NormalClass00PK!%+  Abstract/ID/generate_sid-i.rinu[U:RDoc::AnyMethod[iI"generate_sid:EFI"-Rack::Session::Abstract::ID#generate_sid;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"BGenerate a new session id using Ruby #rand. The size of the ;TI"6session id is controlled by the :sidbits option. ;TI"GMonkey patch this to use custom methods for session id generation.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(secure = @sid_secure);T@FI"ID;TcRDoc::NormalClass00PK!QʻAbstract/ID/get_session-i.rinu[U:RDoc::AnyMethod[iI"get_session:EFI",Rack::Session::Abstract::ID#get_session;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [ I"KAll thread safety and session retrieval procedures should occur here. ;TI"*Should return [session_id, session]. ;TI"HIf nil is provided as the session id, generation of a new valid id ;TI"should occur within.;T: @fileI"$lib/rack/session/abstract/id.rb;T:0@omit_headings_from_table_of_contents_below000[I"(env, sid);T@FI"ID;TcRDoc::NormalClass00PK! Abstract/ID/default_options-i.rinu[U:RDoc::Attr[ iI"default_options:EFI"0Rack::Session::Abstract::ID#default_options;FI"R;F: publico:RDoc::Markup::Document: @parts[: @file0FI"$lib/rack/session/abstract/id.rb;FPK!` Abstract/ID/destroy_session-i.rinu[U:RDoc::AnyMethod[iI"destroy_session:EFI"0Rack::Session::Abstract::ID#destroy_session;TF: privateo:RDoc::Markup::Document: @parts[o:RDoc::Markup::Paragraph; [I"IAll thread safety and session destroy procedures should occur here. ;TI"driver = $driver; } public function open(string $save_path, string $name): bool { return $this->driver->open($save_path, $name); } public function close(): bool { return $this->driver->close(); } #[\ReturnTypeWillChange] public function read(string $id): mixed { return $this->driver->read($id); } public function write(string $id, string $data): bool { return $this->driver->write($id, $data); } public function destroy(string $id): bool { return $this->driver->destroy($id); } #[\ReturnTypeWillChange] public function gc(int $maxlifetime): mixed { return $this->driver->gc($maxlifetime); } public function updateTimestamp(string $id, string$data): bool { return $this->driver->updateTimestamp($id, $data); } public function validateId(string $id): bool { return $this->driver->validateId($id); } } PK!2 OldSessionWrapper.phpnu[driver = $driver; } public function open($save_path, $name) { return $this->driver->open($save_path, $name); } public function close() { return $this->driver->close(); } public function read($id) { return $this->driver->read($id); } public function write($id, $data) { return $this->driver->write($id, $data); } public function destroy($id) { return $this->driver->destroy($id); } public function gc($maxlifetime) { return $this->driver->gc($maxlifetime); } public function updateTimestamp($id, $data) { return $this->driver->updateTimestamp($id, $data); } public function validateId($id) { return $this->driver->validateId($id); } } PK!I#bCI_Session_driver_interface.phpnu[ Class: Rack::Session::Memcache

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Memcache

Rack::Session::Memcache provides simple cookie based session management. Session data is stored in memcached. The corresponding session key is maintained in the cookie. You may treat Session::Memcache as you would Session::Pool with the following caveats.

  • Setting :expire_after to 0 would note to the Memcache server to hang onto the session data until it would drop it according to it's own specifications. However, the cookie sent to the client would expire immediately.

Note that memcache does drop data before it may be listed to expire. For a full description of behaviour, please see memcache's documentation.

Constants

DEFAULT_OPTIONS

Attributes

mutex[R]
pool[R]

Public Class Methods

new(app, options={}) click to toggle source
# File lib/rack/session/memcache.rb, line 29
def initialize(app, options={})
  super

  @mutex = Mutex.new
  mserv = @default_options[:memcache_server]
  mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }

  @pool = options[:cache] || MemCache.new(mserv, mopts)
  unless @pool.active? and @pool.servers.any?{|c| c.alive? }
    raise 'No memcache servers'
  end
end

Public Instance Methods

destroy_session(env, session_id, options) click to toggle source
# File lib/rack/session/memcache.rb, line 71
def destroy_session(env, session_id, options)
  with_lock(env) do
    @pool.delete(session_id)
    generate_sid unless options[:drop]
  end
end
generate_sid() click to toggle source
# File lib/rack/session/memcache.rb, line 42
def generate_sid
  loop do
    sid = super
    break sid unless @pool.get(sid, true)
  end
end
get_session(env, sid) click to toggle source
# File lib/rack/session/memcache.rb, line 49
def get_session(env, sid)
  with_lock(env) do
    unless sid and session = @pool.get(sid)
      sid, session = generate_sid, {}
      unless /^STORED/ =~ @pool.add(sid, session)
        raise "Session collision on '#{sid.inspect}'"
      end
    end
    [sid, session]
  end
end
set_session(env, session_id, new_session, options) click to toggle source
# File lib/rack/session/memcache.rb, line 61
def set_session(env, session_id, new_session, options)
  expiry = options[:expire_after]
  expiry = expiry.nil? ? 0 : expiry + 1

  with_lock(env) do
    @pool.set session_id, new_session, expiry
    session_id
  end
end
with_lock(env) click to toggle source
# File lib/rack/session/memcache.rb, line 78
def with_lock(env)
  @mutex.lock if env['rack.multithread']
  yield
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
  if $VERBOSE
    warn "#{self} is unable to find memcached server."
    warn $!.inspect
  end
  raise
ensure
  @mutex.unlock if @mutex.locked?
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!*+IQaQa Pool.htmlnu[ Class: Rack::Session::Pool

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Pool

Rack::Session::Pool provides simple cookie based session management. Session data is stored in a hash held by @pool. In the context of a multithreaded environment, sessions being committed to the pool is done in a merging manner.

The :drop option is available in rack.session.options if you wish to explicitly remove the session from the session cache.

Example:

myapp = MyRackApp.new
sessioned = Rack::Session::Pool.new(myapp,
  :domain => 'foo.com',
  :expire_after => 2592000
)
Rack::Handler::WEBrick.run sessioned

Constants

DEFAULT_OPTIONS

Attributes

mutex[R]
pool[R]

Public Class Methods

new(app, options={}) click to toggle source
# File lib/rack/session/pool.rb, line 31
def initialize(app, options={})
  super
  @pool = Hash.new
  @mutex = Mutex.new
end

Public Instance Methods

destroy_session(env, session_id, options) click to toggle source
# File lib/rack/session/pool.rb, line 61
def destroy_session(env, session_id, options)
  with_lock(env) do
    @pool.delete(session_id)
    generate_sid unless options[:drop]
  end
end
generate_sid() click to toggle source
# File lib/rack/session/pool.rb, line 37
def generate_sid
  loop do
    sid = super
    break sid unless @pool.key? sid
  end
end
get_session(env, sid) click to toggle source
# File lib/rack/session/pool.rb, line 44
def get_session(env, sid)
  with_lock(env) do
    unless sid and session = @pool[sid]
      sid, session = generate_sid, {}
      @pool.store sid, session
    end
    [sid, session]
  end
end
set_session(env, session_id, new_session, options) click to toggle source
# File lib/rack/session/pool.rb, line 54
def set_session(env, session_id, new_session, options)
  with_lock(env) do
    @pool.store session_id, new_session
    session_id
  end
end
with_lock(env) click to toggle source
# File lib/rack/session/pool.rb, line 68
def with_lock(env)
  @mutex.lock if env['rack.multithread']
  yield
ensure
  @mutex.unlock if @mutex.locked?
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!B3V5V5 Abstract.htmlnu[ Module: Rack::Session::Abstract

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Abstract

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!#+FAFACookie/Base64/Marshal.htmlnu[ Class: Rack::Session::Cookie::Base64::Marshal

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Cookie::Base64::Marshal

Encode session cookies as Marshaled Base64 data

Public Instance Methods

decode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 64
def decode(str)
  return unless str
  ::Marshal.load(super(str)) rescue nil
end
encode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 60
def encode(str)
  super(::Marshal.dump(str))
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!xvkBBCookie/Base64/JSON.htmlnu[ Class: Rack::Session::Cookie::Base64::JSON

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Cookie::Base64::JSON

N.B. Unlike other encoding methods, the contained objects must be a valid JSON composite type, either a Hash or an Array.

Public Instance Methods

decode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 77
def decode(str)
  return unless str
  ::Rack::Utils::OkJson.decode(super(str)) rescue nil
end
encode(obj) click to toggle source
# File lib/rack/session/cookie.rb, line 73
def encode(obj)
  super(::Rack::Utils::OkJson.encode(obj))
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!qqCqCCookie/Base64/ZipJSON.htmlnu[ Class: Rack::Session::Cookie::Base64::ZipJSON

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Cookie::Base64::ZipJSON

Public Instance Methods

decode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 88
def decode(str)
  return unless str
  ::Rack::Utils::OkJson.decode(Zlib::Inflate.inflate(super(str)))
rescue
  nil
end
encode(obj) click to toggle source
# File lib/rack/session/cookie.rb, line 84
def encode(obj)
  super(Zlib::Deflate.deflate(::Rack::Utils::OkJson.encode(obj)))
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!l:@@Cookie/Base64.htmlnu[ Class: Rack::Session::Cookie::Base64

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Cookie::Base64

Encode session cookies as Base64

Public Instance Methods

decode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 54
def decode(str)
  str.unpack('m').first
end
encode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 50
def encode(str)
  [str].pack('m')
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!?r==Cookie/Identity.htmlnu[ Class: Rack::Session::Cookie::Identity

Parent

Methods

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Cookie::Identity

Use no encoding for session cookies

Public Instance Methods

decode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 100
def decode(str); str; end
encode(str) click to toggle source
# File lib/rack/session/cookie.rb, line 99
def encode(str); str; end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!W#yGyG Cookie.htmlnu[ Class: Rack::Session::Cookie

Parent

Methods

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Cookie

Rack::Session::Cookie provides simple cookie based session management. By default, the session is a Ruby Hash stored as base64 encoded marshalled data set to :key (default: rack.session). The object that encodes the session data is configurable and must respond to encode and decode. Both methods must take a string and return a string.

When the secret key is set, cookie data is checked for data integrity. The old secret key is also accepted and allows graceful secret rotation.

Example:

use Rack::Session::Cookie, :key => 'rack.session',
                           :domain => 'foo.com',
                           :path => '/',
                           :expire_after => 2592000,
                           :secret => 'change_me',
                           :old_secret => 'also_change_me'

All parameters are optional.

Example of a cookie with no encoding:

Rack::Session::Cookie.new(application, {
  :coder => Rack::Session::Cookie::Identity.new
})

Example of a cookie with custom encoding:

Rack::Session::Cookie.new(application, {
  :coder => Class.new {
    def encode(str); str.reverse; end
    def decode(str); str.reverse; end
  }.new
})

Attributes

coder[R]

Public Class Methods

new(app, options={}) click to toggle source
# File lib/rack/session/cookie.rb, line 105
def initialize(app, options={})
  @secrets = options.values_at(:secret, :old_secret).compact
  warn         SECURITY WARNING: No secret option provided to Rack::Session::Cookie.        This poses a security threat. It is strongly recommended that you        provide a secret to prevent exploits that may be possible from crafted        cookies. This will not be supported in future versions of Rack, and        future versions will even invalidate your existing user cookies.        Called from: #{caller[0]}. unless @secrets.size >= 1
  @coder  = options[:coder] ||= Base64::Marshal.new
  super(app, options.merge!(:cookie_only => true))
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!ڙtPSPSAbstract/ID.htmlnu[ Class: Rack::Session::Abstract::ID

Parent

Methods

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Abstract::ID

ID sets up a basic framework for implementing an id based sessioning service. Cookies sent to the client for maintaining sessions will only contain an id reference. Only get_session and set_session are required to be overwritten.

All parameters are optional.

  • :key determines the name of the cookie, by default it is 'rack.session'

  • :path, :domain, :expire_after, :secure, and :httponly set the related cookie options as by Rack::Response#add_cookie

  • :skip will not a set a cookie in the response nor update the session state

  • :defer will not set a cookie in the response but still update the session state if it is used with a backend

  • :renew (implementation dependent) will prompt the generation of a new session id, and migration of data to be referenced at the new id. If :defer is set, it will be overridden and the cookie will be set.

  • :sidbits sets the number of bits in length that a generated session id will be.

These options can be set on a per request basis, at the location of env. Additionally the id of the session can be found within the options hash at the key :id. It is highly not recommended to change its value.

Is Rack::Utils::Context compatible.

Not included by default; you must require 'rack/session/abstract/id' to use.

Constants

DEFAULT_OPTIONS

Attributes

default_options[R]
key[R]

Public Class Methods

new(app, options={}) click to toggle source
# File lib/rack/session/abstract/id.rb, line 211
def initialize(app, options={})
  @app = app
  @default_options = self.class::DEFAULT_OPTIONS.merge(options)
  @key = @default_options.delete(:key)
  @cookie_only = @default_options.delete(:cookie_only)
  initialize_sid
end

Public Instance Methods

call(env) click to toggle source
# File lib/rack/session/abstract/id.rb, line 219
def call(env)
  context(env)
end
context(env, app=@app) click to toggle source
# File lib/rack/session/abstract/id.rb, line 223
def context(env, app=@app)
  prepare_session(env)
  status, headers, body = app.call(env)
  commit_session(env, status, headers, body)
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!ȖAbstract/SessionHash.htmlnu[ Class: Rack::Session::Abstract::SessionHash

Files

Class/Module Index [+]

Quicksearch

Rack::Session::Abstract::SessionHash

SessionHash is responsible to lazily load the session from store.

Attributes

id[W]

Public Class Methods

find(env) click to toggle source
# File lib/rack/session/abstract/id.rb, line 27
def self.find(env)
  env[ENV_SESSION_KEY]
end
new(store, env) click to toggle source
# File lib/rack/session/abstract/id.rb, line 39
def initialize(store, env)
  @store = store
  @env = env
  @loaded = false
end
set(env, session) click to toggle source
# File lib/rack/session/abstract/id.rb, line 31
def self.set(env, session)
  env[ENV_SESSION_KEY] = session
end
set_options(env, options) click to toggle source
# File lib/rack/session/abstract/id.rb, line 35
def self.set_options(env, options)
  env[ENV_SESSION_OPTIONS_KEY] = options.dup
end

Public Instance Methods

[](key) click to toggle source
# File lib/rack/session/abstract/id.rb, line 59
def [](key)
  load_for_read!
  @data[key.to_s]
end
Also aliased as: fetch
[]=(key, value) click to toggle source
# File lib/rack/session/abstract/id.rb, line 72
def []=(key, value)
  load_for_write!
  @data[key.to_s] = value
end
Also aliased as: store
clear() click to toggle source
# File lib/rack/session/abstract/id.rb, line 78
def clear
  load_for_write!
  @data.clear
end
delete(key) click to toggle source
# File lib/rack/session/abstract/id.rb, line 104
def delete(key)
  load_for_write!
  @data.delete(key.to_s)
end
destroy() click to toggle source
# File lib/rack/session/abstract/id.rb, line 83
def destroy
  clear
  @id = @store.send(:destroy_session, @env, id, options)
end
each(&block) click to toggle source
# File lib/rack/session/abstract/id.rb, line 54
def each(&block)
  load_for_read!
  @data.each(&block)
end
empty?() click to toggle source
# File lib/rack/session/abstract/id.rb, line 127
def empty?
  load_for_read!
  @data.empty?
end
exists?() click to toggle source
# File lib/rack/session/abstract/id.rb, line 117
def exists?
  return @exists if instance_variable_defined?(:@exists)
  @data = {}
  @exists = @store.send(:session_exists?, @env)
end
fetch(key) click to toggle source
Alias for: []
has_key?(key) click to toggle source
# File lib/rack/session/abstract/id.rb, line 65
def has_key?(key)
  load_for_read!
  @data.has_key?(key.to_s)
end
Also aliased as: key?, include?
id() click to toggle source
# File lib/rack/session/abstract/id.rb, line 45
def id
  return @id if @loaded or instance_variable_defined?(:@id)
  @id = @store.send(:extract_session_id, @env)
end
include?(key) click to toggle source
Alias for: has_key?
inspect() click to toggle source
# File lib/rack/session/abstract/id.rb, line 109
def inspect
  if loaded?
    @data.inspect
  else
    "#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
  end
end
key?(key) click to toggle source
Alias for: has_key?
keys() click to toggle source
# File lib/rack/session/abstract/id.rb, line 132
def keys
  @data.keys
end
loaded?() click to toggle source
# File lib/rack/session/abstract/id.rb, line 123
def loaded?
  @loaded
end
merge!(hash) click to toggle source
Alias for: update
options() click to toggle source
# File lib/rack/session/abstract/id.rb, line 50
def options
  @env[ENV_SESSION_OPTIONS_KEY]
end
replace(hash) click to toggle source
# File lib/rack/session/abstract/id.rb, line 99
def replace(hash)
  load_for_write!
  @data.replace(stringify_keys(hash))
end
store(key, value) click to toggle source
Alias for: []=
to_hash() click to toggle source
# File lib/rack/session/abstract/id.rb, line 88
def to_hash
  load_for_read!
  @data.dup
end
update(hash) click to toggle source
# File lib/rack/session/abstract/id.rb, line 93
def update(hash)
  load_for_write!
  @data.update(stringify_keys(hash))
end
Also aliased as: merge!
values() click to toggle source
# File lib/rack/session/abstract/id.rb, line 136
def values
  @data.values
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.

PK!sR## update-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Store session data on the server. For some session storage types, this is a no-op. full_name: CGI::Session#update is_singleton: false name: update params: () visibility: public PK!ķ  MemoryStore/update-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Update session state. - !ruby/struct:SM::Flow::P body: A no-op. full_name: CGI::Session::MemoryStore#update is_singleton: false name: update params: () visibility: public PK!"MemoryStore/cdesc-MemoryStore.yamlnu[--- !ruby/object:RI::ClassDescription attributes: [] class_methods: - !ruby/object:RI::MethodSummary name: new comment: - !ruby/struct:SM::Flow::P body: In-memory session storage class. - !ruby/struct:SM::Flow::P body: Implements session storage as a global in-memory hash. Session data will only persist for as long as the ruby interpreter instance does. constants: [] full_name: CGI::Session::MemoryStore includes: [] instance_methods: - !ruby/object:RI::MethodSummary name: close - !ruby/object:RI::MethodSummary name: delete - !ruby/object:RI::MethodSummary name: restore - !ruby/object:RI::MethodSummary name: update name: MemoryStore superclass: Object PK!IcMemoryStore/delete-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Delete the session state. full_name: CGI::Session::MemoryStore#delete is_singleton: false name: delete params: () visibility: public PK!u>::MemoryStore/restore-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Restore session state. - !ruby/struct:SM::Flow::P body: Returns session data as a hash. full_name: CGI::Session::MemoryStore#restore is_singleton: false name: restore params: () visibility: public PK!MemoryStore/new-c.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Create a new MemoryStore instance. - !ruby/struct:SM::Flow::P body: session is the session this instance is associated with. option is a list of initialisation options. None are currently recognised. full_name: CGI::Session::MemoryStore::new is_singleton: true name: new params: (session, option=nil) visibility: public PK!uTMemoryStore/close-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Close session storage. - !ruby/struct:SM::Flow::P body: A no-op. full_name: CGI::Session::MemoryStore#close is_singleton: false name: close params: () visibility: public PK!Ue %5b%5d-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Retrieve the session data for key key. full_name: CGI::Session#[] is_singleton: false name: "[]" params: (key) visibility: public PK!7{22cdesc-Session.yamlnu[--- !ruby/object:RI::ClassDescription attributes: - !ruby/object:RI::Attribute comment: - !ruby/struct:SM::Flow::P body: The id of this session. name: new_session rw: R - !ruby/object:RI::Attribute comment: - !ruby/struct:SM::Flow::P body: The id of this session. name: session_id rw: R class_methods: - !ruby/object:RI::MethodSummary name: new comment: - !ruby/struct:SM::Flow::P body: Class representing an HTTP session. See documentation for the file cgi/session.rb for an introduction to HTTP sessions. - !ruby/struct:SM::Flow::H level: 2 text: Lifecycle - !ruby/struct:SM::Flow::P body: "A CGI::Session instance is created from a CGI object. By default, this CGI::Session instance will start a new session if none currently exists, or continue the current session for this client if one does exist. The new_session option can be used to either always or never create a new session. See #new() for more details." - !ruby/struct:SM::Flow::P body: "#delete() deletes a session from session storage. It does not however remove the session id from the client. If the client makes another request with the same id, the effect will be to start a new session with the old session's id." - !ruby/struct:SM::Flow::H level: 2 text: Setting and retrieving session data. - !ruby/struct:SM::Flow::P body: The Session class associates data with a session as key-value pairs. This data can be set and retrieved by indexing the Session instance using '[]', much the same as hashes (although other hash methods are not supported). - !ruby/struct:SM::Flow::P body: When session processing has been completed for a request, the session should be closed using the close() method. This will store the session's state to persistent storage. If you want to store the session's state to persistent storage without finishing session processing for this request, call the update() method. - !ruby/struct:SM::Flow::H level: 2 text: Storing session state - !ruby/struct:SM::Flow::P body: "The caller can specify what form of storage to use for the session's data with the database_manager option to CGI::Session::new. The following storage classes are provided as part of the standard library:" - !ruby/object:SM::Flow::LIST contents: - !ruby/struct:SM::Flow::LI label: "CGI::Session::FileStore:" body: stores data as plain text in a flat file. Only works with String data. This is the default storage type. - !ruby/struct:SM::Flow::LI label: "CGI::Session::MemoryStore:" body: stores data in an in-memory hash. The data only persists for as long as the current ruby interpreter instance does. - !ruby/struct:SM::Flow::LI label: "CGI::Session::PStore:" body: stores data in Marshalled format. Provided by cgi/session/pstore.rb. Supports data of any type, and provides file-locking and transaction support. type: :NOTE - !ruby/struct:SM::Flow::P body: "Custom storage types can also be created by defining a class with the following methods:" - !ruby/struct:SM::Flow::VERB body: " new(session, options)\n restore # returns hash of session data.\n update\n close\n delete\n" - !ruby/struct:SM::Flow::P body: Changing storage type mid-session does not work. Note in particular that by default the FileStore and PStore session data files have the same name. If your application switches from one to the other without making sure that filenames will be different and clients still have old sessions lying around in cookies, then things will break nastily! - !ruby/struct:SM::Flow::H level: 2 text: Maintaining the session id. - !ruby/struct:SM::Flow::P body: Most session state is maintained on the server. However, a session id must be passed backwards and forwards between client and server to maintain a reference to this session state. - !ruby/struct:SM::Flow::P body: The simplest way to do this is via cookies. The CGI::Session class provides transparent support for session id communication via cookies if the client has cookies enabled. - !ruby/struct:SM::Flow::P body: If the client has cookies disabled, the session id must be included as a parameter of all requests sent by the client to the server. The CGI::Session class in conjunction with the CGI class will transparently add the session id as a hidden input field to all forms generated using the CGI#form() HTML generation method. No built-in support is provided for other mechanisms, such as URL re-writing. The caller is responsible for extracting the session id from the session_id attribute and manually encoding it in URLs and adding it as a hidden input to HTML forms created by other mechanisms. Also, session expiry is not automatically handled. - !ruby/struct:SM::Flow::H level: 2 text: Examples of use - !ruby/struct:SM::Flow::H level: 3 text: Setting the user's name - !ruby/struct:SM::Flow::VERB body: " require 'cgi'\n require 'cgi/session'\n require 'cgi/session/pstore' # provides CGI::Session::PStore\n\n cgi = CGI.new("html4")\n\n session = CGI::Session.new(cgi,\n 'database_manager' => CGI::Session::PStore, # use PStore\n 'session_key' => '_rb_sess_id', # custom session key\n 'session_expires' => Time.now + 30 * 60, # 30 minute timeout\n 'prefix' => 'pstore_sid_') # PStore option\n if cgi.has_key?('user_name') and cgi['user_name'] != ''\n # coerce to String: cgi[] returns the\n # string-like CGI::QueryExtension::Value\n session['user_name'] = cgi['user_name'].to_s\n elsif !session['user_name']\n session['user_name'] = "guest"\n end\n session.close\n" - !ruby/struct:SM::Flow::H level: 3 text: Creating a new session safely - !ruby/struct:SM::Flow::VERB body: " require 'cgi'\n require 'cgi/session'\n\n cgi = CGI.new("html4")\n\n # We make sure to delete an old session if one exists,\n # not just to free resources, but to prevent the session\n # from being maliciously hijacked later on.\n begin\n session = CGI::Session.new(cgi, 'new_session' => false)\n session.delete\n rescue ArgumentError # if no old session\n end\n session = CGI::Session.new(cgi, 'new_session' => true)\n session.close\n" constants: [] full_name: CGI::Session includes: [] instance_methods: - !ruby/object:RI::MethodSummary name: "[]" - !ruby/object:RI::MethodSummary name: "[]=" - !ruby/object:RI::MethodSummary name: close - !ruby/object:RI::MethodSummary name: create_new_id - !ruby/object:RI::MethodSummary name: delete - !ruby/object:RI::MethodSummary name: update name: Session superclass: Object PK! delete-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Delete the session from storage. Also closes the storage. - !ruby/struct:SM::Flow::P body: Note that the session's data is not automatically deleted upon the session expiring. full_name: CGI::Session#delete is_singleton: false name: delete params: () visibility: public PK! FileStore/update-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Save session state to the session's FileStore file. full_name: CGI::Session::FileStore#update is_singleton: false name: update params: () visibility: public PK!  FileStore/delete-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Close and delete the session's FileStore file. full_name: CGI::Session::FileStore#delete is_singleton: false name: delete params: () visibility: public PK!f__FileStore/restore-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Restore session state from the session's FileStore file. - !ruby/struct:SM::Flow::P body: Returns the session state as a hash. full_name: CGI::Session::FileStore#restore is_singleton: false name: restore params: () visibility: public PK!$XFileStore/new-c.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Create a new FileStore instance. - !ruby/struct:SM::Flow::P body: This constructor is used internally by CGI::Session. The user does not generally need to call it directly. - !ruby/struct:SM::Flow::P body: session is the session for which this instance is being created. The session id must only contain alphanumeric characters; automatically generated session ids observe this requirement. - !ruby/struct:SM::Flow::P body: "option is a hash of options for the initializer. The following options are recognised:" - !ruby/object:SM::Flow::LIST contents: - !ruby/struct:SM::Flow::LI label: "tmpdir:" body: the directory to use for storing the FileStore file. Defaults to Dir::tmpdir (generally "/tmp" on Unix systems). - !ruby/struct:SM::Flow::LI label: "prefix:" body: the prefix to add to the session id when generating the filename for this session's FileStore file. Defaults to the empty string. - !ruby/struct:SM::Flow::LI label: "suffix:" body: the prefix to add to the session id when generating the filename for this session's FileStore file. Defaults to the empty string. type: :NOTE - !ruby/struct:SM::Flow::P body: This session's FileStore file will be created if it does not exist, or opened if it does. full_name: CGI::Session::FileStore::new is_singleton: true name: new params: (session, option={}) visibility: public PK!.7<FileStore/close-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Update and close the session's FileStore file. full_name: CGI::Session::FileStore#close is_singleton: false name: close params: () visibility: public PK!i  FileStore/cdesc-FileStore.yamlnu[--- !ruby/object:RI::ClassDescription attributes: [] class_methods: - !ruby/object:RI::MethodSummary name: new comment: - !ruby/struct:SM::Flow::P body: File-based session storage class. - !ruby/struct:SM::Flow::P body: Implements session storage as a flat file of 'key=value' values. This storage type only works directly with String values; the user is responsible for converting other types to Strings when storing and from Strings when retrieving. constants: [] full_name: CGI::Session::FileStore includes: [] instance_methods: - !ruby/object:RI::MethodSummary name: close - !ruby/object:RI::MethodSummary name: delete - !ruby/object:RI::MethodSummary name: restore - !ruby/object:RI::MethodSummary name: update name: FileStore superclass: Object PK!a* * new-c.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Create a new CGI::Session object for request. - !ruby/struct:SM::Flow::P body: "request is an instance of the CGI class (see cgi.rb). option is a hash of options for initialising this CGI::Session instance. The following options are recognised:" - !ruby/object:SM::Flow::LIST contents: - !ruby/struct:SM::Flow::LI label: "session_key:" body: the parameter name used for the session id. Defaults to '_session_id'. - !ruby/struct:SM::Flow::LI label: "session_id:" body: the session id to use. If not provided, then it is retrieved from the session_key parameter of the request, or automatically generated for a new session. - !ruby/struct:SM::Flow::LI label: "new_session:" body: if true, force creation of a new session. If not set, a new session is only created if none currently exists. If false, a new session is never created, and if none currently exists and the session_id option is not set, an ArgumentError is raised. - !ruby/struct:SM::Flow::LI label: "database_manager:" body: the name of the class providing storage facilities for session state persistence. Built-in support is provided for FileStore (the default), MemoryStore, and PStore (from cgi/session/pstore.rb). See the documentation for these classes for more details. type: :NOTE - !ruby/struct:SM::Flow::P body: The following options are also recognised, but only apply if the session id is stored in a cookie. - !ruby/object:SM::Flow::LIST contents: - !ruby/struct:SM::Flow::LI label: "session_expires:" body: the time the current session expires, as a Time object. If not set, the session will terminate when the user's browser is closed. - !ruby/struct:SM::Flow::LI label: "session_domain:" body: the hostname domain for which this session is valid. If not set, defaults to the hostname of the server. - !ruby/struct:SM::Flow::LI label: "session_secure:" body: if true, this session will only work over HTTPS. - !ruby/struct:SM::Flow::LI label: "session_path:" body: the path for which this session applies. Defaults to the directory of the CGI script. type: :NOTE - !ruby/struct:SM::Flow::P body: option is also passed on to the session storage class initializer; see the documentation for each session storage class for the options they support. - !ruby/struct:SM::Flow::P body: The retrieved or created session is automatically added to request as a cookie, and also to its output_hidden table, which is used to add hidden input elements to forms. - !ruby/struct:SM::Flow::P body: WARNING the output_hidden fields are surrounded by a <fieldset> tag in HTML 4 generation, which is not invisible on many browsers; you may wish to disable the use of fieldsets with code similar to the following (see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/37805) - !ruby/struct:SM::Flow::VERB body: " cgi = CGI.new("html4")\n class << cgi\n undef_method :fieldset\n end\n" full_name: CGI::Session::new is_singleton: true name: new params: (request, option={}) visibility: public PK!Rcreate_new_id-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Create a new session id. - !ruby/struct:SM::Flow::P body: The session id is an MD5 hash based upon the time, a random number, and a constant string. This routine is used internally for automatically generated session ids. full_name: CGI::Session#create_new_id is_singleton: false name: create_new_id params: () visibility: private PK!9п?? close-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Store session data on the server and close the session storage. For some session storage types, this is a no-op. full_name: CGI::Session#close is_singleton: false name: close params: () visibility: public PK!rևNoSession/cdesc-NoSession.yamlnu[--- !ruby/object:RI::ClassDescription attributes: [] class_methods: [] comment: constants: [] full_name: CGI::Session::NoSession includes: [] instance_methods: [] name: NoSession superclass: RuntimeError PK!乤T%5b%5d%3d-i.yamlnu[--- !ruby/object:RI::MethodDescription aliases: [] block_params: comment: - !ruby/struct:SM::Flow::P body: Set the session date for key key. full_name: CGI::Session#[]= is_singleton: false name: "[]=" params: (key, val) visibility: public PK!G yySessionBagProxy.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session; /** * @author Nicolas Grekas * * @internal */ final class SessionBagProxy implements SessionBagInterface { private $bag; private $data; private $usageIndex; public function __construct(SessionBagInterface $bag, array &$data, ?int &$usageIndex) { $this->bag = $bag; $this->data = &$data; $this->usageIndex = &$usageIndex; } public function getBag(): SessionBagInterface { ++$this->usageIndex; return $this->bag; } public function isEmpty(): bool { if (!isset($this->data[$this->bag->getStorageKey()])) { return true; } ++$this->usageIndex; return empty($this->data[$this->bag->getStorageKey()]); } /** * {@inheritdoc} */ public function getName(): string { return $this->bag->getName(); } /** * {@inheritdoc} */ public function initialize(array &$array): void { ++$this->usageIndex; $this->data[$this->bag->getStorageKey()] = &$array; $this->bag->initialize($array); } /** * {@inheritdoc} */ public function getStorageKey(): string { return $this->bag->getStorageKey(); } /** * {@inheritdoc} */ public function clear() { return $this->bag->clear(); } } PK!, !$Attribute/NamespacedAttributeBag.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Attribute; /** * This class provides structured storage of session attributes using * a name spacing character in the key. * * @author Drak */ class NamespacedAttributeBag extends AttributeBag { private $namespaceCharacter; /** * @param string $storageKey Session storage key * @param string $namespaceCharacter Namespace character to use in keys */ public function __construct(string $storageKey = '_sf2_attributes', string $namespaceCharacter = '/') { $this->namespaceCharacter = $namespaceCharacter; parent::__construct($storageKey); } /** * {@inheritdoc} */ public function has($name) { // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is $attributes = $this->resolveAttributePath($name); $name = $this->resolveKey($name); if (null === $attributes) { return false; } return \array_key_exists($name, $attributes); } /** * {@inheritdoc} */ public function get($name, $default = null) { // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is $attributes = $this->resolveAttributePath($name); $name = $this->resolveKey($name); if (null === $attributes) { return $default; } return \array_key_exists($name, $attributes) ? $attributes[$name] : $default; } /** * {@inheritdoc} */ public function set($name, $value) { $attributes = &$this->resolveAttributePath($name, true); $name = $this->resolveKey($name); $attributes[$name] = $value; } /** * {@inheritdoc} */ public function remove($name) { $retval = null; $attributes = &$this->resolveAttributePath($name); $name = $this->resolveKey($name); if (null !== $attributes && \array_key_exists($name, $attributes)) { $retval = $attributes[$name]; unset($attributes[$name]); } return $retval; } /** * Resolves a path in attributes property and returns it as a reference. * * This method allows structured namespacing of session attributes. * * @param string $name Key name * @param bool $writeContext Write context, default false * * @return array|null */ protected function &resolveAttributePath($name, $writeContext = false) { $array = &$this->attributes; $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name; // Check if there is anything to do, else return if (!$name) { return $array; } $parts = explode($this->namespaceCharacter, $name); if (\count($parts) < 2) { if (!$writeContext) { return $array; } $array[$parts[0]] = []; return $array; } unset($parts[\count($parts) - 1]); foreach ($parts as $part) { if (null !== $array && !\array_key_exists($part, $array)) { if (!$writeContext) { $null = null; return $null; } $array[$part] = []; } $array = &$array[$part]; } return $array; } /** * Resolves the key from the name. * * This is the last part in a dot separated string. * * @param string $name * * @return string */ protected function resolveKey($name) { if (false !== $pos = strrpos($name, $this->namespaceCharacter)) { $name = substr($name, $pos + 1); } return $name; } } PK!T T Attribute/AttributeBag.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Attribute; /** * This class relates to session attribute storage. */ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable { private $name = 'attributes'; private $storageKey; protected $attributes = []; /** * @param string $storageKey The key used to store attributes in the session */ public function __construct(string $storageKey = '_sf2_attributes') { $this->storageKey = $storageKey; } /** * {@inheritdoc} */ public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } /** * {@inheritdoc} */ public function initialize(array &$attributes) { $this->attributes = &$attributes; } /** * {@inheritdoc} */ public function getStorageKey() { return $this->storageKey; } /** * {@inheritdoc} */ public function has($name) { return \array_key_exists($name, $this->attributes); } /** * {@inheritdoc} */ public function get($name, $default = null) { return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; } /** * {@inheritdoc} */ public function set($name, $value) { $this->attributes[$name] = $value; } /** * {@inheritdoc} */ public function all() { return $this->attributes; } /** * {@inheritdoc} */ public function replace(array $attributes) { $this->attributes = []; foreach ($attributes as $key => $value) { $this->set($key, $value); } } /** * {@inheritdoc} */ public function remove($name) { $retval = null; if (\array_key_exists($name, $this->attributes)) { $retval = $this->attributes[$name]; unset($this->attributes[$name]); } return $retval; } /** * {@inheritdoc} */ public function clear() { $return = $this->attributes; $this->attributes = []; return $return; } /** * Returns an iterator for attributes. * * @return \ArrayIterator An \ArrayIterator instance */ public function getIterator() { return new \ArrayIterator($this->attributes); } /** * Returns the number of attributes. * * @return int The number of attributes */ public function count() { return \count($this->attributes); } } PK!O UO#Attribute/AttributeBagInterface.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Attribute; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; /** * Attributes store. * * @author Drak */ interface AttributeBagInterface extends SessionBagInterface { /** * Checks if an attribute is defined. * * @param string $name The attribute name * * @return bool true if the attribute is defined, false otherwise */ public function has($name); /** * Returns an attribute. * * @param string $name The attribute name * @param mixed $default The default value if not found * * @return mixed */ public function get($name, $default = null); /** * Sets an attribute. * * @param string $name * @param mixed $value */ public function set($name, $value); /** * Returns attributes. * * @return array */ public function all(); public function replace(array $attributes); /** * Removes an attribute. * * @param string $name * * @return mixed The removed value or null when it does not exist */ public function remove($name); } PK!2 Flash/FlashBag.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Flash; /** * FlashBag flash message container. * * @author Drak */ class FlashBag implements FlashBagInterface { private $name = 'flashes'; private $flashes = []; private $storageKey; /** * @param string $storageKey The key used to store flashes in the session */ public function __construct(string $storageKey = '_symfony_flashes') { $this->storageKey = $storageKey; } /** * {@inheritdoc} */ public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } /** * {@inheritdoc} */ public function initialize(array &$flashes) { $this->flashes = &$flashes; } /** * {@inheritdoc} */ public function add($type, $message) { $this->flashes[$type][] = $message; } /** * {@inheritdoc} */ public function peek($type, array $default = []) { return $this->has($type) ? $this->flashes[$type] : $default; } /** * {@inheritdoc} */ public function peekAll() { return $this->flashes; } /** * {@inheritdoc} */ public function get($type, array $default = []) { if (!$this->has($type)) { return $default; } $return = $this->flashes[$type]; unset($this->flashes[$type]); return $return; } /** * {@inheritdoc} */ public function all() { $return = $this->peekAll(); $this->flashes = []; return $return; } /** * {@inheritdoc} */ public function set($type, $messages) { $this->flashes[$type] = (array) $messages; } /** * {@inheritdoc} */ public function setAll(array $messages) { $this->flashes = $messages; } /** * {@inheritdoc} */ public function has($type) { return \array_key_exists($type, $this->flashes) && $this->flashes[$type]; } /** * {@inheritdoc} */ public function keys() { return array_keys($this->flashes); } /** * {@inheritdoc} */ public function getStorageKey() { return $this->storageKey; } /** * {@inheritdoc} */ public function clear() { return $this->all(); } } PK!}0c c Flash/AutoExpireFlashBag.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Flash; /** * AutoExpireFlashBag flash message container. * * @author Drak */ class AutoExpireFlashBag implements FlashBagInterface { private $name = 'flashes'; private $flashes = ['display' => [], 'new' => []]; private $storageKey; /** * @param string $storageKey The key used to store flashes in the session */ public function __construct(string $storageKey = '_symfony_flashes') { $this->storageKey = $storageKey; } /** * {@inheritdoc} */ public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } /** * {@inheritdoc} */ public function initialize(array &$flashes) { $this->flashes = &$flashes; // The logic: messages from the last request will be stored in new, so we move them to previous // This request we will show what is in 'display'. What is placed into 'new' this time round will // be moved to display next time round. $this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : []; $this->flashes['new'] = []; } /** * {@inheritdoc} */ public function add($type, $message) { $this->flashes['new'][$type][] = $message; } /** * {@inheritdoc} */ public function peek($type, array $default = []) { return $this->has($type) ? $this->flashes['display'][$type] : $default; } /** * {@inheritdoc} */ public function peekAll() { return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : []; } /** * {@inheritdoc} */ public function get($type, array $default = []) { $return = $default; if (!$this->has($type)) { return $return; } if (isset($this->flashes['display'][$type])) { $return = $this->flashes['display'][$type]; unset($this->flashes['display'][$type]); } return $return; } /** * {@inheritdoc} */ public function all() { $return = $this->flashes['display']; $this->flashes['display'] = []; return $return; } /** * {@inheritdoc} */ public function setAll(array $messages) { $this->flashes['new'] = $messages; } /** * {@inheritdoc} */ public function set($type, $messages) { $this->flashes['new'][$type] = (array) $messages; } /** * {@inheritdoc} */ public function has($type) { return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type]; } /** * {@inheritdoc} */ public function keys() { return array_keys($this->flashes['display']); } /** * {@inheritdoc} */ public function getStorageKey() { return $this->storageKey; } /** * {@inheritdoc} */ public function clear() { return $this->all(); } } PK!v$Flash/FlashBagInterface.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Flash; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; /** * FlashBagInterface. * * @author Drak */ interface FlashBagInterface extends SessionBagInterface { /** * Adds a flash message for the given type. * * @param string $type * @param mixed $message */ public function add($type, $message); /** * Registers one or more messages for a given type. * * @param string $type * @param string|array $messages */ public function set($type, $messages); /** * Gets flash messages for a given type. * * @param string $type Message category type * @param array $default Default value if $type does not exist * * @return array */ public function peek($type, array $default = []); /** * Gets all flash messages. * * @return array */ public function peekAll(); /** * Gets and clears flash from the stack. * * @param string $type * @param array $default Default value if $type does not exist * * @return array */ public function get($type, array $default = []); /** * Gets and clears flashes from the stack. * * @return array */ public function all(); /** * Sets all flash messages. */ public function setAll(array $messages); /** * Has flash messages for a given type? * * @param string $type * * @return bool */ public function has($type); /** * Returns a list of all defined types. * * @return array */ public function keys(); } PK!1SessionInterface.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session; use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; /** * Interface for the session. * * @author Drak */ interface SessionInterface { /** * Starts the session storage. * * @return bool * * @throws \RuntimeException if session fails to start */ public function start(); /** * Returns the session ID. * * @return string */ public function getId(); /** * Sets the session ID. * * @param string $id */ public function setId($id); /** * Returns the session name. * * @return string */ public function getName(); /** * Sets the session name. * * @param string $name */ public function setName($name); /** * Invalidates the current session. * * Clears all session attributes and flashes and regenerates the * session and deletes the old session from persistence. * * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value * will leave the system settings unchanged, 0 sets the cookie * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. * * @return bool */ public function invalidate($lifetime = null); /** * Migrates the current session to a new session id while maintaining all * session attributes. * * @param bool $destroy Whether to delete the old session or leave it to garbage collection * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value * will leave the system settings unchanged, 0 sets the cookie * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. * * @return bool */ public function migrate($destroy = false, $lifetime = null); /** * Force the session to be saved and closed. * * This method is generally not required for real sessions as * the session will be automatically saved at the end of * code execution. */ public function save(); /** * Checks if an attribute is defined. * * @param string $name The attribute name * * @return bool */ public function has($name); /** * Returns an attribute. * * @param string $name The attribute name * @param mixed $default The default value if not found * * @return mixed */ public function get($name, $default = null); /** * Sets an attribute. * * @param string $name * @param mixed $value */ public function set($name, $value); /** * Returns attributes. * * @return array */ public function all(); /** * Sets attributes. */ public function replace(array $attributes); /** * Removes an attribute. * * @param string $name * * @return mixed The removed value or null when it does not exist */ public function remove($name); /** * Clears all attributes. */ public function clear(); /** * Checks if the session was started. * * @return bool */ public function isStarted(); /** * Registers a SessionBagInterface with the session. */ public function registerBag(SessionBagInterface $bag); /** * Gets a bag instance by name. * * @param string $name * * @return SessionBagInterface */ public function getBag($name); /** * Gets session meta. * * @return MetadataBag */ public function getMetadataBag(); } PK!tb"Storage/MockFileSessionStorage.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage; /** * MockFileSessionStorage is used to mock sessions for * functional testing when done in a single PHP process. * * No PHP session is actually started since a session can be initialized * and shutdown only once per PHP execution cycle and this class does * not pollute any session related globals, including session_*() functions * or session.* PHP ini directives. * * @author Drak */ class MockFileSessionStorage extends MockArraySessionStorage { private $savePath; /** * @param string $savePath Path of directory to save session files * @param string $name Session name */ public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null) { if (null === $savePath) { $savePath = sys_get_temp_dir(); } if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) { throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s".', $savePath)); } $this->savePath = $savePath; parent::__construct($name, $metaBag); } /** * {@inheritdoc} */ public function start() { if ($this->started) { return true; } if (!$this->id) { $this->id = $this->generateId(); } $this->read(); $this->started = true; return true; } /** * {@inheritdoc} */ public function regenerate($destroy = false, $lifetime = null) { if (!$this->started) { $this->start(); } if ($destroy) { $this->destroy(); } return parent::regenerate($destroy, $lifetime); } /** * {@inheritdoc} */ public function save() { if (!$this->started) { throw new \RuntimeException('Trying to save a session that was not started yet or was already closed.'); } $data = $this->data; foreach ($this->bags as $bag) { if (empty($data[$key = $bag->getStorageKey()])) { unset($data[$key]); } } if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) { unset($data[$key]); } try { if ($data) { file_put_contents($this->getFilePath(), serialize($data)); } else { $this->destroy(); } } finally { $this->data = $data; } // this is needed for Silex, where the session object is re-used across requests // in functional tests. In Symfony, the container is rebooted, so we don't have // this issue $this->started = false; } /** * Deletes a session from persistent storage. * Deliberately leaves session data in memory intact. */ private function destroy(): void { if (is_file($this->getFilePath())) { unlink($this->getFilePath()); } } /** * Calculate path to file. */ private function getFilePath(): string { return $this->savePath.'/'.$this->id.'.mocksess'; } /** * Reads session from storage and loads session. */ private function read(): void { $filePath = $this->getFilePath(); $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : []; $this->loadSession(); } } PK!5#Storage/SessionStorageInterface.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; /** * StorageInterface. * * @author Fabien Potencier * @author Drak */ interface SessionStorageInterface { /** * Starts the session. * * @return bool True if started * * @throws \RuntimeException if something goes wrong starting the session */ public function start(); /** * Checks if the session is started. * * @return bool True if started, false otherwise */ public function isStarted(); /** * Returns the session ID. * * @return string The session ID or empty */ public function getId(); /** * Sets the session ID. * * @param string $id */ public function setId($id); /** * Returns the session name. * * @return mixed The session name */ public function getName(); /** * Sets the session name. * * @param string $name */ public function setName($name); /** * Regenerates id that represents this storage. * * This method must invoke session_regenerate_id($destroy) unless * this interface is used for a storage object designed for unit * or functional testing where a real PHP session would interfere * with testing. * * Note regenerate+destroy should not clear the session data in memory * only delete the session data from persistent storage. * * Care: When regenerating the session ID no locking is involved in PHP's * session design. See https://bugs.php.net/61470 for a discussion. * So you must make sure the regenerated session is saved BEFORE sending the * headers with the new ID. Symfony's HttpKernel offers a listener for this. * See Symfony\Component\HttpKernel\EventListener\SaveSessionListener. * Otherwise session data could get lost again for concurrent requests with the * new ID. One result could be that you get logged out after just logging in. * * @param bool $destroy Destroy session when regenerating? * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value * will leave the system settings unchanged, 0 sets the cookie * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. * * @return bool True if session regenerated, false if error * * @throws \RuntimeException If an error occurs while regenerating this storage */ public function regenerate($destroy = false, $lifetime = null); /** * Force the session to be saved and closed. * * This method must invoke session_write_close() unless this interface is * used for a storage object design for unit or functional testing where * a real PHP session would interfere with testing, in which case * it should actually persist the session data if required. * * @throws \RuntimeException if the session is saved without being started, or if the session * is already closed */ public function save(); /** * Clear all session data in memory. */ public function clear(); /** * Gets a SessionBagInterface by name. * * @param string $name * * @return SessionBagInterface * * @throws \InvalidArgumentException If the bag does not exist */ public function getBag($name); /** * Registers a SessionBagInterface for use. */ public function registerBag(SessionBagInterface $bag); /** * @return MetadataBag */ public function getMetadataBag(); } PK! )Storage/Handler/MongoDbSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Session handler using the mongodb/mongodb package and MongoDB driver extension. * * @author Markus Bachmann * * @see https://packagist.org/packages/mongodb/mongodb * @see https://php.net/mongodb */ class MongoDbSessionHandler extends AbstractSessionHandler { private $mongo; /** * @var \MongoDB\Collection */ private $collection; /** * @var array */ private $options; /** * Constructor. * * List of available options: * * database: The name of the database [required] * * collection: The name of the collection [required] * * id_field: The field name for storing the session id [default: _id] * * data_field: The field name for storing the session data [default: data] * * time_field: The field name for storing the timestamp [default: time] * * expiry_field: The field name for storing the expiry-timestamp [default: expires_at]. * * It is strongly recommended to put an index on the `expiry_field` for * garbage-collection. Alternatively it's possible to automatically expire * the sessions in the database as described below: * * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions * automatically. Such an index can for example look like this: * * db..ensureIndex( * { "": 1 }, * { "expireAfterSeconds": 0 } * ) * * More details on: https://docs.mongodb.org/manual/tutorial/expire-data/ * * If you use such an index, you can drop `gc_probability` to 0 since * no garbage-collection is required. * * @throws \InvalidArgumentException When "database" or "collection" not provided */ public function __construct(\MongoDB\Client $mongo, array $options) { if (!isset($options['database']) || !isset($options['collection'])) { throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler.'); } $this->mongo = $mongo; $this->options = array_merge([ 'id_field' => '_id', 'data_field' => 'data', 'time_field' => 'time', 'expiry_field' => 'expires_at', ], $options); } /** * @return bool */ public function close() { return true; } /** * {@inheritdoc} */ protected function doDestroy($sessionId) { $this->getCollection()->deleteOne([ $this->options['id_field'] => $sessionId, ]); return true; } /** * @return bool */ public function gc($maxlifetime) { $this->getCollection()->deleteMany([ $this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()], ]); return true; } /** * {@inheritdoc} */ protected function doWrite($sessionId, $data) { $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); $fields = [ $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), $this->options['expiry_field'] => $expiry, $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY), ]; $this->getCollection()->updateOne( [$this->options['id_field'] => $sessionId], ['$set' => $fields], ['upsert' => true] ); return true; } /** * @return bool */ public function updateTimestamp($sessionId, $data) { $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); $this->getCollection()->updateOne( [$this->options['id_field'] => $sessionId], ['$set' => [ $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), $this->options['expiry_field'] => $expiry, ]] ); return true; } /** * {@inheritdoc} */ protected function doRead($sessionId) { $dbData = $this->getCollection()->findOne([ $this->options['id_field'] => $sessionId, $this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()], ]); if (null === $dbData) { return ''; } return $dbData[$this->options['data_field']]->getData(); } private function getCollection(): \MongoDB\Collection { if (null === $this->collection) { $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']); } return $this->collection; } /** * @return \MongoDB\Client */ protected function getMongo() { return $this->mongo; } } PK!I' (Storage/Handler/StrictSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`. * * @author Nicolas Grekas */ class StrictSessionHandler extends AbstractSessionHandler { private $handler; private $doDestroy; public function __construct(\SessionHandlerInterface $handler) { if ($handler instanceof \SessionUpdateTimestampHandlerInterface) { throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', \get_class($handler), self::class)); } $this->handler = $handler; } /** * @return bool */ public function open($savePath, $sessionName) { parent::open($savePath, $sessionName); return $this->handler->open($savePath, $sessionName); } /** * {@inheritdoc} */ protected function doRead($sessionId) { return $this->handler->read($sessionId); } /** * @return bool */ public function updateTimestamp($sessionId, $data) { return $this->write($sessionId, $data); } /** * {@inheritdoc} */ protected function doWrite($sessionId, $data) { return $this->handler->write($sessionId, $data); } /** * @return bool */ public function destroy($sessionId) { $this->doDestroy = true; $destroyed = parent::destroy($sessionId); return $this->doDestroy ? $this->doDestroy($sessionId) : $destroyed; } /** * {@inheritdoc} */ protected function doDestroy($sessionId) { $this->doDestroy = false; return $this->handler->destroy($sessionId); } /** * @return bool */ public function close() { return $this->handler->close(); } /** * @return bool */ public function gc($maxlifetime) { return $this->handler->gc($maxlifetime); } } PK!~*Storage/Handler/AbstractSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; use Symfony\Component\HttpFoundation\Session\SessionUtils; /** * This abstract session handler provides a generic implementation * of the PHP 7.0 SessionUpdateTimestampHandlerInterface, * enabling strict and lazy session handling. * * @author Nicolas Grekas */ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface { private $sessionName; private $prefetchId; private $prefetchData; private $newSessionId; private $igbinaryEmptyData; /** * @return bool */ public function open($savePath, $sessionName) { $this->sessionName = $sessionName; if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) { header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire'))); } return true; } /** * @param string $sessionId * * @return string */ abstract protected function doRead($sessionId); /** * @param string $sessionId * @param string $data * * @return bool */ abstract protected function doWrite($sessionId, $data); /** * @param string $sessionId * * @return bool */ abstract protected function doDestroy($sessionId); /** * @return bool */ public function validateId($sessionId) { $this->prefetchData = $this->read($sessionId); $this->prefetchId = $sessionId; return '' !== $this->prefetchData; } /** * @return string */ public function read($sessionId) { if (null !== $this->prefetchId) { $prefetchId = $this->prefetchId; $prefetchData = $this->prefetchData; $this->prefetchId = $this->prefetchData = null; if ($prefetchId === $sessionId || '' === $prefetchData) { $this->newSessionId = '' === $prefetchData ? $sessionId : null; return $prefetchData; } } $data = $this->doRead($sessionId); $this->newSessionId = '' === $data ? $sessionId : null; return $data; } /** * @return bool */ public function write($sessionId, $data) { if (null === $this->igbinaryEmptyData) { // see https://github.com/igbinary/igbinary/issues/146 $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize([]) : ''; } if ('' === $data || $this->igbinaryEmptyData === $data) { return $this->destroy($sessionId); } $this->newSessionId = null; return $this->doWrite($sessionId, $data); } /** * @return bool */ public function destroy($sessionId) { if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) { if (!$this->sessionName) { throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', static::class)); } $cookie = SessionUtils::popSessionCookie($this->sessionName, $sessionId); /* * We send an invalidation Set-Cookie header (zero lifetime) * when either the session was started or a cookie with * the session name was sent by the client (in which case * we know it's invalid as a valid session cookie would've * started the session). */ if (null === $cookie || isset($_COOKIE[$this->sessionName])) { if (\PHP_VERSION_ID < 70300) { setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN)); } else { $params = session_get_cookie_params(); unset($params['lifetime']); setcookie($this->sessionName, '', $params); } } } return $this->newSessionId === $sessionId || $this->doDestroy($sessionId); } } PK!ˋ&Storage/Handler/NullSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Can be used in unit testing or in a situations where persisted sessions are not desired. * * @author Drak */ class NullSessionHandler extends AbstractSessionHandler { /** * @return bool */ public function close() { return true; } /** * @return bool */ public function validateId($sessionId) { return true; } /** * {@inheritdoc} */ protected function doRead($sessionId) { return ''; } /** * @return bool */ public function updateTimestamp($sessionId, $data) { return true; } /** * {@inheritdoc} */ protected function doWrite($sessionId, $data) { return true; } /** * {@inheritdoc} */ protected function doDestroy($sessionId) { return true; } /** * @return bool */ public function gc($maxlifetime) { return true; } } PK!?d[)Storage/Handler/SessionHandlerFactory.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; use Doctrine\DBAL\DriverManager; use Symfony\Component\Cache\Adapter\AbstractAdapter; use Symfony\Component\Cache\Traits\RedisClusterProxy; use Symfony\Component\Cache\Traits\RedisProxy; /** * @author Nicolas Grekas */ class SessionHandlerFactory { /** * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy|\Memcached|\PDO|string $connection Connection or DSN */ public static function createHandler($connection): AbstractSessionHandler { if (!\is_string($connection) && !\is_object($connection)) { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a string or a connection object, "%s" given.', __METHOD__, \gettype($connection))); } switch (true) { case $connection instanceof \Redis: case $connection instanceof \RedisArray: case $connection instanceof \RedisCluster: case $connection instanceof \Predis\ClientInterface: case $connection instanceof RedisProxy: case $connection instanceof RedisClusterProxy: return new RedisSessionHandler($connection); case $connection instanceof \Memcached: return new MemcachedSessionHandler($connection); case $connection instanceof \PDO: return new PdoSessionHandler($connection); case !\is_string($connection): throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', \get_class($connection))); case 0 === strpos($connection, 'file://'): return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7))); case 0 === strpos($connection, 'redis:'): case 0 === strpos($connection, 'rediss:'): case 0 === strpos($connection, 'memcached:'): if (!class_exists(AbstractAdapter::class)) { throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $connection)); } $handlerClass = 0 === strpos($connection, 'memcached:') ? MemcachedSessionHandler::class : RedisSessionHandler::class; $connection = AbstractAdapter::createConnection($connection, ['lazy' => true]); return new $handlerClass($connection); case 0 === strpos($connection, 'pdo_oci://'): if (!class_exists(DriverManager::class)) { throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection)); } $connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection(); // no break; case 0 === strpos($connection, 'mssql://'): case 0 === strpos($connection, 'mysql://'): case 0 === strpos($connection, 'mysql2://'): case 0 === strpos($connection, 'pgsql://'): case 0 === strpos($connection, 'postgres://'): case 0 === strpos($connection, 'postgresql://'): case 0 === strpos($connection, 'sqlsrv://'): case 0 === strpos($connection, 'sqlite://'): case 0 === strpos($connection, 'sqlite3://'): return new PdoSessionHandler($connection); } throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', $connection)); } } PK!qE~  'Storage/Handler/RedisSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; use Predis\Response\ErrorInterface; use Symfony\Component\Cache\Traits\RedisClusterProxy; use Symfony\Component\Cache\Traits\RedisProxy; /** * Redis based session storage handler based on the Redis class * provided by the PHP redis extension. * * @author Dalibor Karlović */ class RedisSessionHandler extends AbstractSessionHandler { private $redis; /** * @var string Key prefix for shared environments */ private $prefix; /** * @var int Time to live in seconds */ private $ttl; /** * List of available options: * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server * * ttl: The time to live in seconds. * * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis * * @throws \InvalidArgumentException When unsupported client or options are passed */ public function __construct($redis, array $options = []) { if ( !$redis instanceof \Redis && !$redis instanceof \RedisArray && !$redis instanceof \RedisCluster && !$redis instanceof \Predis\ClientInterface && !$redis instanceof RedisProxy && !$redis instanceof RedisClusterProxy ) { throw new \InvalidArgumentException(sprintf('"%s()" expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, "%s" given.', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); } if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) { throw new \InvalidArgumentException(sprintf('The following options are not supported "%s".', implode(', ', $diff))); } $this->redis = $redis; $this->prefix = $options['prefix'] ?? 'sf_s'; $this->ttl = $options['ttl'] ?? null; } /** * {@inheritdoc} */ protected function doRead($sessionId): string { return $this->redis->get($this->prefix.$sessionId) ?: ''; } /** * {@inheritdoc} */ protected function doWrite($sessionId, $data): bool { $result = $this->redis->setEx($this->prefix.$sessionId, (int) ($this->ttl ?? ini_get('session.gc_maxlifetime')), $data); return $result && !$result instanceof ErrorInterface; } /** * {@inheritdoc} */ protected function doDestroy($sessionId): bool { $this->redis->del($this->prefix.$sessionId); return true; } /** * {@inheritdoc} */ public function close(): bool { return true; } /** * {@inheritdoc} */ public function gc($maxlifetime): bool { return true; } /** * @return bool */ public function updateTimestamp($sessionId, $data) { return (bool) $this->redis->expire($this->prefix.$sessionId, (int) ($this->ttl ?? ini_get('session.gc_maxlifetime'))); } } PK! +Storage/Handler/MigratingSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Migrating session handler for migrating from one handler to another. It reads * from the current handler and writes both the current and new ones. * * It ignores errors from the new handler. * * @author Ross Motley * @author Oliver Radwell */ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface { private $currentHandler; private $writeOnlyHandler; public function __construct(\SessionHandlerInterface $currentHandler, \SessionHandlerInterface $writeOnlyHandler) { if (!$currentHandler instanceof \SessionUpdateTimestampHandlerInterface) { $currentHandler = new StrictSessionHandler($currentHandler); } if (!$writeOnlyHandler instanceof \SessionUpdateTimestampHandlerInterface) { $writeOnlyHandler = new StrictSessionHandler($writeOnlyHandler); } $this->currentHandler = $currentHandler; $this->writeOnlyHandler = $writeOnlyHandler; } /** * @return bool */ public function close() { $result = $this->currentHandler->close(); $this->writeOnlyHandler->close(); return $result; } /** * @return bool */ public function destroy($sessionId) { $result = $this->currentHandler->destroy($sessionId); $this->writeOnlyHandler->destroy($sessionId); return $result; } /** * @return bool */ public function gc($maxlifetime) { $result = $this->currentHandler->gc($maxlifetime); $this->writeOnlyHandler->gc($maxlifetime); return $result; } /** * @return bool */ public function open($savePath, $sessionName) { $result = $this->currentHandler->open($savePath, $sessionName); $this->writeOnlyHandler->open($savePath, $sessionName); return $result; } /** * @return string */ public function read($sessionId) { // No reading from new handler until switch-over return $this->currentHandler->read($sessionId); } /** * @return bool */ public function write($sessionId, $sessionData) { $result = $this->currentHandler->write($sessionId, $sessionData); $this->writeOnlyHandler->write($sessionId, $sessionData); return $result; } /** * @return bool */ public function validateId($sessionId) { // No reading from new handler until switch-over return $this->currentHandler->validateId($sessionId); } /** * @return bool */ public function updateTimestamp($sessionId, $sessionData) { $result = $this->currentHandler->updateTimestamp($sessionId, $sessionData); $this->writeOnlyHandler->updateTimestamp($sessionId, $sessionData); return $result; } } PK!%Storage/Handler/PdoSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Session handler using a PDO connection to read and write data. * * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements * different locking strategies to handle concurrent access to the same session. * Locking is necessary to prevent loss of data due to race conditions and to keep * the session data consistent between read() and write(). With locking, requests * for the same session will wait until the other one finished writing. For this * reason it's best practice to close a session as early as possible to improve * concurrency. PHPs internal files session handler also implements locking. * * Attention: Since SQLite does not support row level locks but locks the whole database, * it means only one session can be accessed at a time. Even different sessions would wait * for another to finish. So saving session in SQLite should only be considered for * development or prototypes. * * Session data is a binary string that can contain non-printable characters like the null byte. * For this reason it must be saved in a binary column in the database like BLOB in MySQL. * Saving it in a character column could corrupt the data. You can use createTable() * to initialize a correctly defined table. * * @see https://php.net/sessionhandlerinterface * * @author Fabien Potencier * @author Michael Williams * @author Tobias Schultze */ class PdoSessionHandler extends AbstractSessionHandler { /** * No locking is done. This means sessions are prone to loss of data due to * race conditions of concurrent requests to the same session. The last session * write will win in this case. It might be useful when you implement your own * logic to deal with this like an optimistic approach. */ const LOCK_NONE = 0; /** * Creates an application-level lock on a session. The disadvantage is that the * lock is not enforced by the database and thus other, unaware parts of the * application could still concurrently modify the session. The advantage is it * does not require a transaction. * This mode is not available for SQLite and not yet implemented for oci and sqlsrv. */ const LOCK_ADVISORY = 1; /** * Issues a real row lock. Since it uses a transaction between opening and * closing a session, you have to be careful when you use same database connection * that you also use for your application logic. This mode is the default because * it's the only reliable solution across DBMSs. */ const LOCK_TRANSACTIONAL = 2; private const MAX_LIFETIME = 315576000; /** * @var \PDO|null PDO instance or null when not connected yet */ private $pdo; /** * @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled */ private $dsn = false; /** * @var string Database driver */ private $driver; /** * @var string Table name */ private $table = 'sessions'; /** * @var string Column for session id */ private $idCol = 'sess_id'; /** * @var string Column for session data */ private $dataCol = 'sess_data'; /** * @var string Column for lifetime */ private $lifetimeCol = 'sess_lifetime'; /** * @var string Column for timestamp */ private $timeCol = 'sess_time'; /** * @var string Username when lazy-connect */ private $username = ''; /** * @var string Password when lazy-connect */ private $password = ''; /** * @var array Connection options when lazy-connect */ private $connectionOptions = []; /** * @var int The strategy for locking, see constants */ private $lockMode = self::LOCK_TRANSACTIONAL; /** * It's an array to support multiple reads before closing which is manual, non-standard usage. * * @var \PDOStatement[] An array of statements to release advisory locks */ private $unlockStatements = []; /** * @var bool True when the current session exists but expired according to session.gc_maxlifetime */ private $sessionExpired = false; /** * @var bool Whether a transaction is active */ private $inTransaction = false; /** * @var bool Whether gc() has been called */ private $gcCalled = false; /** * You can either pass an existing database connection as PDO instance or * pass a DSN string that will be used to lazy-connect to the database * when the session is actually used. Furthermore it's possible to pass null * which will then use the session.save_path ini setting as PDO DSN parameter. * * List of available options: * * db_table: The name of the table [default: sessions] * * db_id_col: The column where to store the session id [default: sess_id] * * db_data_col: The column where to store the session data [default: sess_data] * * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime] * * db_time_col: The column where to store the timestamp [default: sess_time] * * db_username: The username when lazy-connect [default: ''] * * db_password: The password when lazy-connect [default: ''] * * db_connection_options: An array of driver-specific connection options [default: []] * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL] * * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null * * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION */ public function __construct($pdoOrDsn = null, array $options = []) { if ($pdoOrDsn instanceof \PDO) { if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__)); } $this->pdo = $pdoOrDsn; $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); } elseif (\is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) { $this->dsn = $this->buildDsnFromUrl($pdoOrDsn); } else { $this->dsn = $pdoOrDsn; } $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table; $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol; $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol; $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol; $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol; $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username; $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password; $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions; $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode; } /** * Creates the table to store sessions which can be called once for setup. * * Session ID is saved in a column of maximum length 128 because that is enough even * for a 512 bit configured session.hash_function like Whirlpool. Session data is * saved in a BLOB. One could also use a shorter inlined varbinary column * if one was sure the data fits into it. * * @throws \PDOException When the table already exists * @throws \DomainException When an unsupported PDO driver is used */ public function createTable() { // connect if we are not yet $this->getConnection(); switch ($this->driver) { case 'mysql': // We use varbinary for the ID column because it prevents unwanted conversions: // - character set conversions between server and client // - trailing space removal // - case-insensitivity // - language processing like é == e $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB"; break; case 'sqlite': $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; break; case 'pgsql': $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; break; case 'oci': $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; break; case 'sqlsrv': $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; break; default: throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver)); } try { $this->pdo->exec($sql); $this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)"); } catch (\PDOException $e) { $this->rollback(); throw $e; } } /** * Returns true when the current session exists but expired according to session.gc_maxlifetime. * * Can be used to distinguish between a new session and one that expired due to inactivity. * * @return bool Whether current session expired */ public function isSessionExpired() { return $this->sessionExpired; } /** * @return bool */ public function open($savePath, $sessionName) { $this->sessionExpired = false; if (null === $this->pdo) { $this->connect($this->dsn ?: $savePath); } return parent::open($savePath, $sessionName); } /** * @return string */ public function read($sessionId) { try { return parent::read($sessionId); } catch (\PDOException $e) { $this->rollback(); throw $e; } } /** * @return bool */ public function gc($maxlifetime) { // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process. // This way, pruning expired sessions does not block them from being started while the current session is used. $this->gcCalled = true; return true; } /** * {@inheritdoc} */ protected function doDestroy($sessionId) { // delete the record associated with this id $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; try { $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->execute(); } catch (\PDOException $e) { $this->rollback(); throw $e; } return true; } /** * {@inheritdoc} */ protected function doWrite($sessionId, $data) { $maxlifetime = (int) ini_get('session.gc_maxlifetime'); try { // We use a single MERGE SQL query when supported by the database. $mergeStmt = $this->getMergeStatement($sessionId, $data, $maxlifetime); if (null !== $mergeStmt) { $mergeStmt->execute(); return true; } $updateStmt = $this->getUpdateStatement($sessionId, $data, $maxlifetime); $updateStmt->execute(); // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior). // We can just catch such an error and re-execute the update. This is similar to a serializable // transaction with retry logic on serialization failures but without the overhead and without possible // false positives due to longer gap locking. if (!$updateStmt->rowCount()) { try { $insertStmt = $this->getInsertStatement($sessionId, $data, $maxlifetime); $insertStmt->execute(); } catch (\PDOException $e) { // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys if (0 === strpos($e->getCode(), '23')) { $updateStmt->execute(); } else { throw $e; } } } } catch (\PDOException $e) { $this->rollback(); throw $e; } return true; } /** * @return bool */ public function updateTimestamp($sessionId, $data) { $expiry = time() + (int) ini_get('session.gc_maxlifetime'); try { $updateStmt = $this->pdo->prepare( "UPDATE $this->table SET $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id" ); $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $updateStmt->bindParam(':expiry', $expiry, \PDO::PARAM_INT); $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); $updateStmt->execute(); } catch (\PDOException $e) { $this->rollback(); throw $e; } return true; } /** * @return bool */ public function close() { $this->commit(); while ($unlockStmt = array_shift($this->unlockStatements)) { $unlockStmt->execute(); } if ($this->gcCalled) { $this->gcCalled = false; // delete the session records that have expired $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time AND $this->lifetimeCol > :min"; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); $stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT); $stmt->execute(); // to be removed in 6.0 if ('mysql' === $this->driver) { $legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol + $this->timeCol < :time"; } else { $legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol < :time - $this->timeCol"; } $stmt = $this->pdo->prepare($legacySql); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); $stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT); $stmt->execute(); } if (false !== $this->dsn) { $this->pdo = null; // only close lazy-connection } return true; } /** * Lazy-connects to the database. */ private function connect(string $dsn): void { $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions); $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); } /** * Builds a PDO DSN from a URL-like connection string. * * @todo implement missing support for oci DSN (which look totally different from other PDO ones) */ private function buildDsnFromUrl(string $dsnOrUrl): string { // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl); $params = parse_url($url); if (false === $params) { return $dsnOrUrl; // If the URL is not valid, let's assume it might be a DSN already. } $params = array_map('rawurldecode', $params); // Override the default username and password. Values passed through options will still win over these in the constructor. if (isset($params['user'])) { $this->username = $params['user']; } if (isset($params['pass'])) { $this->password = $params['pass']; } if (!isset($params['scheme'])) { throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler.'); } $driverAliasMap = [ 'mssql' => 'sqlsrv', 'mysql2' => 'mysql', // Amazon RDS, for some weird reason 'postgres' => 'pgsql', 'postgresql' => 'pgsql', 'sqlite3' => 'sqlite', ]; $driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme']; // Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here. if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) { $driver = substr($driver, 4); } switch ($driver) { case 'mysql': case 'pgsql': $dsn = $driver.':'; if (isset($params['host']) && '' !== $params['host']) { $dsn .= 'host='.$params['host'].';'; } if (isset($params['port']) && '' !== $params['port']) { $dsn .= 'port='.$params['port'].';'; } if (isset($params['path'])) { $dbName = substr($params['path'], 1); // Remove the leading slash $dsn .= 'dbname='.$dbName.';'; } return $dsn; case 'sqlite': return 'sqlite:'.substr($params['path'], 1); case 'sqlsrv': $dsn = 'sqlsrv:server='; if (isset($params['host'])) { $dsn .= $params['host']; } if (isset($params['port']) && '' !== $params['port']) { $dsn .= ','.$params['port']; } if (isset($params['path'])) { $dbName = substr($params['path'], 1); // Remove the leading slash $dsn .= ';Database='.$dbName; } return $dsn; default: throw new \InvalidArgumentException(sprintf('The scheme "%s" is not supported by the PdoSessionHandler URL configuration. Pass a PDO DSN directly.', $params['scheme'])); } } /** * Helper method to begin a transaction. * * Since SQLite does not support row level locks, we have to acquire a reserved lock * on the database immediately. Because of https://bugs.php.net/42766 we have to create * such a transaction manually which also means we cannot use PDO::commit or * PDO::rollback or PDO::inTransaction for SQLite. * * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions * due to https://percona.com/blog/2013/12/12/one-more-innodb-gap-lock-to-avoid/ . * So we change it to READ COMMITTED. */ private function beginTransaction(): void { if (!$this->inTransaction) { if ('sqlite' === $this->driver) { $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); } else { if ('mysql' === $this->driver) { $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); } $this->pdo->beginTransaction(); } $this->inTransaction = true; } } /** * Helper method to commit a transaction. */ private function commit(): void { if ($this->inTransaction) { try { // commit read-write transaction which also releases the lock if ('sqlite' === $this->driver) { $this->pdo->exec('COMMIT'); } else { $this->pdo->commit(); } $this->inTransaction = false; } catch (\PDOException $e) { $this->rollback(); throw $e; } } } /** * Helper method to rollback a transaction. */ private function rollback(): void { // We only need to rollback if we are in a transaction. Otherwise the resulting // error would hide the real problem why rollback was called. We might not be // in a transaction when not using the transactional locking behavior or when // two callbacks (e.g. destroy and write) are invoked that both fail. if ($this->inTransaction) { if ('sqlite' === $this->driver) { $this->pdo->exec('ROLLBACK'); } else { $this->pdo->rollBack(); } $this->inTransaction = false; } } /** * Reads the session data in respect to the different locking strategies. * * We need to make sure we do not return session data that is already considered garbage according * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. * * @param string $sessionId Session ID * * @return string The session data */ protected function doRead($sessionId) { if (self::LOCK_ADVISORY === $this->lockMode) { $this->unlockStatements[] = $this->doAdvisoryLock($sessionId); } $selectSql = $this->getSelectSql(); $selectStmt = $this->pdo->prepare($selectSql); $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $insertStmt = null; do { $selectStmt->execute(); $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); if ($sessionRows) { $expiry = (int) $sessionRows[0][1]; if ($expiry <= self::MAX_LIFETIME) { $expiry += $sessionRows[0][2]; } if ($expiry < time()) { $this->sessionExpired = true; return ''; } return \is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0]; } if (null !== $insertStmt) { $this->rollback(); throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.'); } if (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) { // In strict mode, session fixation is not possible: new sessions always start with a unique // random id, so that concurrency is not possible and this code path can be skipped. // Exclusive-reading of non-existent rows does not block, so we need to do an insert to block // until other connections to the session are committed. try { $insertStmt = $this->getInsertStatement($sessionId, '', 0); $insertStmt->execute(); } catch (\PDOException $e) { // Catch duplicate key error because other connection created the session already. // It would only not be the case when the other connection destroyed the session. if (0 === strpos($e->getCode(), '23')) { // Retrieve finished session data written by concurrent connection by restarting the loop. // We have to start a new transaction as a failed query will mark the current transaction as // aborted in PostgreSQL and disallow further queries within it. $this->rollback(); $this->beginTransaction(); continue; } throw $e; } } return ''; } while (true); } /** * Executes an application-level lock on the database. * * @return \PDOStatement The statement that needs to be executed later to release the lock * * @throws \DomainException When an unsupported PDO driver is used * * @todo implement missing advisory locks * - for oci using DBMS_LOCK.REQUEST * - for sqlsrv using sp_getapplock with LockOwner = Session */ private function doAdvisoryLock(string $sessionId): \PDOStatement { switch ($this->driver) { case 'mysql': // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced. $lockId = substr($sessionId, 0, 64); // should we handle the return value? 0 on timeout, null on error // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)'); $stmt->bindValue(':key', $lockId, \PDO::PARAM_STR); $stmt->execute(); $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); $releaseStmt->bindValue(':key', $lockId, \PDO::PARAM_STR); return $releaseStmt; case 'pgsql': // Obtaining an exclusive session level advisory lock requires an integer key. // When session.sid_bits_per_character > 4, the session id can contain non-hex-characters. // So we cannot just use hexdec(). if (4 === \PHP_INT_SIZE) { $sessionInt1 = $this->convertStringToInt($sessionId); $sessionInt2 = $this->convertStringToInt(substr($sessionId, 4, 4)); $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)'); $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); $stmt->execute(); $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)'); $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); } else { $sessionBigInt = $this->convertStringToInt($sessionId); $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)'); $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); $stmt->execute(); $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)'); $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); } return $releaseStmt; case 'sqlite': throw new \DomainException('SQLite does not support advisory locks.'); default: throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver)); } } /** * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer. * * Keep in mind, PHP integers are signed. */ private function convertStringToInt(string $string): int { if (4 === \PHP_INT_SIZE) { return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]); } $int1 = (\ord($string[7]) << 24) + (\ord($string[6]) << 16) + (\ord($string[5]) << 8) + \ord($string[4]); $int2 = (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]); return $int2 + ($int1 << 32); } /** * Return a locking or nonlocking SQL query to read session information. * * @throws \DomainException When an unsupported PDO driver is used */ private function getSelectSql(): string { if (self::LOCK_TRANSACTIONAL === $this->lockMode) { $this->beginTransaction(); // selecting the time column should be removed in 6.0 switch ($this->driver) { case 'mysql': case 'oci': case 'pgsql': return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE"; case 'sqlsrv': return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id"; case 'sqlite': // we already locked when starting transaction break; default: throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver)); } } return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id"; } /** * Returns an insert statement supported by the database for writing session data. */ private function getInsertStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement { switch ($this->driver) { case 'oci': $data = fopen('php://memory', 'r+'); fwrite($data, $sessionData); rewind($data); $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :expiry, :time) RETURNING $this->dataCol into :data"; break; default: $data = $sessionData; $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)"; break; } $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); $stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); return $stmt; } /** * Returns an update statement supported by the database for writing session data. */ private function getUpdateStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement { switch ($this->driver) { case 'oci': $data = fopen('php://memory', 'r+'); fwrite($data, $sessionData); rewind($data); $sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data"; break; default: $data = $sessionData; $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id"; break; } $stmt = $this->pdo->prepare($sql); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); $stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); return $stmt; } /** * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data. */ private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement { switch (true) { case 'mysql' === $this->driver: $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ". "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; break; case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): // MERGE is only available since SQL Server 2008 and must be terminated by semicolon // It also requires HOLDLOCK according to https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/ $mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ". "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ". "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;"; break; case 'sqlite' === $this->driver: $mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)"; break; case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='): $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ". "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; break; default: // MERGE is not supported with LOBs: https://oracle.com/technetwork/articles/fuecks-lobs-095315.html return null; } $mergeStmt = $this->pdo->prepare($mergeSql); if ('sqlsrv' === $this->driver) { $mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB); $mergeStmt->bindValue(4, time() + $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(5, time(), \PDO::PARAM_INT); $mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB); $mergeStmt->bindValue(7, time() + $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(8, time(), \PDO::PARAM_INT); } else { $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB); $mergeStmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT); $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); } return $mergeStmt; } /** * Return a PDO instance. * * @return \PDO */ protected function getConnection() { if (null === $this->pdo) { $this->connect($this->dsn ?: ini_get('session.save_path')); } return $this->pdo; } } PK! ds< < +Storage/Handler/MemcachedSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Memcached based session storage handler based on the Memcached class * provided by the PHP memcached extension. * * @see https://php.net/memcached * * @author Drak */ class MemcachedSessionHandler extends AbstractSessionHandler { private $memcached; /** * @var int Time to live in seconds */ private $ttl; /** * @var string Key prefix for shared environments */ private $prefix; /** * Constructor. * * List of available options: * * prefix: The prefix to use for the memcached keys in order to avoid collision * * expiretime: The time to live in seconds. * * @throws \InvalidArgumentException When unsupported options are passed */ public function __construct(\Memcached $memcached, array $options = []) { $this->memcached = $memcached; if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) { throw new \InvalidArgumentException(sprintf('The following options are not supported "%s".', implode(', ', $diff))); } $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; } /** * @return bool */ public function close() { return $this->memcached->quit(); } /** * {@inheritdoc} */ protected function doRead($sessionId) { return $this->memcached->get($this->prefix.$sessionId) ?: ''; } /** * @return bool */ public function updateTimestamp($sessionId, $data) { $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl); return true; } /** * {@inheritdoc} */ protected function doWrite($sessionId, $data) { return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl); } /** * {@inheritdoc} */ protected function doDestroy($sessionId) { $result = $this->memcached->delete($this->prefix.$sessionId); return $result || \Memcached::RES_NOTFOUND == $this->memcached->getResultCode(); } /** * @return bool */ public function gc($maxlifetime) { // not required here because memcached will auto expire the records anyhow. return true; } /** * Return a Memcached instance. * * @return \Memcached */ protected function getMemcached() { return $this->memcached; } } PK!+8##,Storage/Handler/NativeFileSessionHandler.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** * Native session handler using PHP's built in file storage. * * @author Drak */ class NativeFileSessionHandler extends \SessionHandler { /** * @param string $savePath Path of directory to save session files * Default null will leave setting as defined by PHP. * '/path', 'N;/path', or 'N;octal-mode;/path * * @see https://php.net/session.configuration#ini.session.save-path for further details. * * @throws \InvalidArgumentException On invalid $savePath * @throws \RuntimeException When failing to create the save directory */ public function __construct(string $savePath = null) { if (null === $savePath) { $savePath = ini_get('session.save_path'); } $baseDir = $savePath; if ($count = substr_count($savePath, ';')) { if ($count > 2) { throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'.', $savePath)); } // characters after last ';' are the path $baseDir = ltrim(strrchr($savePath, ';'), ';'); } if ($baseDir && !is_dir($baseDir) && !@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) { throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s".', $baseDir)); } ini_set('session.save_path', $savePath); ini_set('session.save_handler', 'files'); } } PK!M#Storage/MockArraySessionStorage.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; /** * MockArraySessionStorage mocks the session for unit tests. * * No PHP session is actually started since a session can be initialized * and shutdown only once per PHP execution cycle. * * When doing functional testing, you should use MockFileSessionStorage instead. * * @author Fabien Potencier * @author Bulat Shakirzyanov * @author Drak */ class MockArraySessionStorage implements SessionStorageInterface { /** * @var string */ protected $id = ''; /** * @var string */ protected $name; /** * @var bool */ protected $started = false; /** * @var bool */ protected $closed = false; /** * @var array */ protected $data = []; /** * @var MetadataBag */ protected $metadataBag; /** * @var array|SessionBagInterface[] */ protected $bags = []; public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null) { $this->name = $name; $this->setMetadataBag($metaBag); } public function setSessionData(array $array) { $this->data = $array; } /** * {@inheritdoc} */ public function start() { if ($this->started) { return true; } if (empty($this->id)) { $this->id = $this->generateId(); } $this->loadSession(); return true; } /** * {@inheritdoc} */ public function regenerate($destroy = false, $lifetime = null) { if (!$this->started) { $this->start(); } $this->metadataBag->stampNew($lifetime); $this->id = $this->generateId(); return true; } /** * {@inheritdoc} */ public function getId() { return $this->id; } /** * {@inheritdoc} */ public function setId($id) { if ($this->started) { throw new \LogicException('Cannot set session ID after the session has started.'); } $this->id = $id; } /** * {@inheritdoc} */ public function getName() { return $this->name; } /** * {@inheritdoc} */ public function setName($name) { $this->name = $name; } /** * {@inheritdoc} */ public function save() { if (!$this->started || $this->closed) { throw new \RuntimeException('Trying to save a session that was not started yet or was already closed.'); } // nothing to do since we don't persist the session data $this->closed = false; $this->started = false; } /** * {@inheritdoc} */ public function clear() { // clear out the bags foreach ($this->bags as $bag) { $bag->clear(); } // clear out the session $this->data = []; // reconnect the bags to the session $this->loadSession(); } /** * {@inheritdoc} */ public function registerBag(SessionBagInterface $bag) { $this->bags[$bag->getName()] = $bag; } /** * {@inheritdoc} */ public function getBag($name) { if (!isset($this->bags[$name])) { throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name)); } if (!$this->started) { $this->start(); } return $this->bags[$name]; } /** * {@inheritdoc} */ public function isStarted() { return $this->started; } public function setMetadataBag(MetadataBag $bag = null) { if (null === $bag) { $bag = new MetadataBag(); } $this->metadataBag = $bag; } /** * Gets the MetadataBag. * * @return MetadataBag */ public function getMetadataBag() { return $this->metadataBag; } /** * Generates a session ID. * * This doesn't need to be particularly cryptographically secure since this is just * a mock. * * @return string */ protected function generateId() { return hash('sha256', uniqid('ss_mock_', true)); } protected function loadSession() { $bags = array_merge($this->bags, [$this->metadataBag]); foreach ($bags as $bag) { $key = $bag->getStorageKey(); $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : []; $bag->initialize($this->data[$key]); } $this->started = true; $this->closed = false; } } PK!RkrXXStorage/MetadataBag.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; /** * Metadata container. * * Adds metadata to the session. * * @author Drak */ class MetadataBag implements SessionBagInterface { const CREATED = 'c'; const UPDATED = 'u'; const LIFETIME = 'l'; /** * @var string */ private $name = '__metadata'; /** * @var string */ private $storageKey; /** * @var array */ protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0]; /** * Unix timestamp. * * @var int */ private $lastUsed; /** * @var int */ private $updateThreshold; /** * @param string $storageKey The key used to store bag in the session * @param int $updateThreshold The time to wait between two UPDATED updates */ public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0) { $this->storageKey = $storageKey; $this->updateThreshold = $updateThreshold; } /** * {@inheritdoc} */ public function initialize(array &$array) { $this->meta = &$array; if (isset($array[self::CREATED])) { $this->lastUsed = $this->meta[self::UPDATED]; $timeStamp = time(); if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) { $this->meta[self::UPDATED] = $timeStamp; } } else { $this->stampCreated(); } } /** * Gets the lifetime that the session cookie was set with. * * @return int */ public function getLifetime() { return $this->meta[self::LIFETIME]; } /** * Stamps a new session's metadata. * * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value * will leave the system settings unchanged, 0 sets the cookie * to expire with browser session. Time is in seconds, and is * not a Unix timestamp. */ public function stampNew($lifetime = null) { $this->stampCreated($lifetime); } /** * {@inheritdoc} */ public function getStorageKey() { return $this->storageKey; } /** * Gets the created timestamp metadata. * * @return int Unix timestamp */ public function getCreated() { return $this->meta[self::CREATED]; } /** * Gets the last used metadata. * * @return int Unix timestamp */ public function getLastUsed() { return $this->lastUsed; } /** * {@inheritdoc} */ public function clear() { // nothing to do } /** * {@inheritdoc} */ public function getName() { return $this->name; } /** * Sets name. * * @param string $name */ public function setName($name) { $this->name = $name; } private function stampCreated(int $lifetime = null): void { $timeStamp = time(); $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp; $this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime; } } PK!+ %Storage/Proxy/SessionHandlerProxy.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; /** * @author Drak */ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface { protected $handler; public function __construct(\SessionHandlerInterface $handler) { $this->handler = $handler; $this->wrapper = ($handler instanceof \SessionHandler); $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; } /** * @return \SessionHandlerInterface */ public function getHandler() { return $this->handler; } // \SessionHandlerInterface /** * @return bool */ public function open($savePath, $sessionName) { return (bool) $this->handler->open($savePath, $sessionName); } /** * @return bool */ public function close() { return (bool) $this->handler->close(); } /** * @return string */ public function read($sessionId) { return (string) $this->handler->read($sessionId); } /** * @return bool */ public function write($sessionId, $data) { return (bool) $this->handler->write($sessionId, $data); } /** * @return bool */ public function destroy($sessionId) { return (bool) $this->handler->destroy($sessionId); } /** * @return bool */ public function gc($maxlifetime) { return (bool) $this->handler->gc($maxlifetime); } /** * @return bool */ public function validateId($sessionId) { return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId); } /** * @return bool */ public function updateTimestamp($sessionId, $data) { return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data); } } PK!ȧ- - Storage/Proxy/AbstractProxy.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; /** * @author Drak */ abstract class AbstractProxy { /** * Flag if handler wraps an internal PHP session handler (using \SessionHandler). * * @var bool */ protected $wrapper = false; /** * @var string */ protected $saveHandlerName; /** * Gets the session.save_handler name. * * @return string|null */ public function getSaveHandlerName() { return $this->saveHandlerName; } /** * Is this proxy handler and instance of \SessionHandlerInterface. * * @return bool */ public function isSessionHandlerInterface() { return $this instanceof \SessionHandlerInterface; } /** * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. * * @return bool */ public function isWrapper() { return $this->wrapper; } /** * Has a session started? * * @return bool */ public function isActive() { return PHP_SESSION_ACTIVE === session_status(); } /** * Gets the session ID. * * @return string */ public function getId() { return session_id(); } /** * Sets the session ID. * * @param string $id * * @throws \LogicException */ public function setId($id) { if ($this->isActive()) { throw new \LogicException('Cannot change the ID of an active session.'); } session_id($id); } /** * Gets the session name. * * @return string */ public function getName() { return session_name(); } /** * Sets the session name. * * @param string $name * * @throws \LogicException */ public function setName($name) { if ($this->isActive()) { throw new \LogicException('Cannot change the name of an active session.'); } session_name($name); } } PK!%-7-7 Storage/NativeSessionStorage.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage; use Symfony\Component\HttpFoundation\Session\SessionBagInterface; use Symfony\Component\HttpFoundation\Session\SessionUtils; use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; // Help opcache.preload discover always-needed symbols class_exists(MetadataBag::class); class_exists(StrictSessionHandler::class); class_exists(SessionHandlerProxy::class); /** * This provides a base class for session attribute storage. * * @author Drak */ class NativeSessionStorage implements SessionStorageInterface { /** * @var SessionBagInterface[] */ protected $bags = []; /** * @var bool */ protected $started = false; /** * @var bool */ protected $closed = false; /** * @var AbstractProxy|\SessionHandlerInterface */ protected $saveHandler; /** * @var MetadataBag */ protected $metadataBag; /** * @var string|null */ private $emulateSameSite; /** * Depending on how you want the storage driver to behave you probably * want to override this constructor entirely. * * List of options for $options array with their defaults. * * @see https://php.net/session.configuration for options * but we omit 'session.' from the beginning of the keys for convenience. * * ("auto_start", is not supported as it tells PHP to start a session before * PHP starts to execute user-land code. Setting during runtime has no effect). * * cache_limiter, "" (use "0" to prevent headers from being sent entirely). * cache_expire, "0" * cookie_domain, "" * cookie_httponly, "" * cookie_lifetime, "0" * cookie_path, "/" * cookie_secure, "" * cookie_samesite, null * gc_divisor, "100" * gc_maxlifetime, "1440" * gc_probability, "1" * lazy_write, "1" * name, "PHPSESSID" * referer_check, "" * serialize_handler, "php" * use_strict_mode, "0" * use_cookies, "1" * use_only_cookies, "1" * use_trans_sid, "0" * upload_progress.enabled, "1" * upload_progress.cleanup, "1" * upload_progress.prefix, "upload_progress_" * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS" * upload_progress.freq, "1%" * upload_progress.min-freq, "1" * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset=" * sid_length, "32" * sid_bits_per_character, "5" * trans_sid_hosts, $_SERVER['HTTP_HOST'] * trans_sid_tags, "a=href,area=href,frame=src,form=" * * @param AbstractProxy|\SessionHandlerInterface|null $handler */ public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null) { if (!\extension_loaded('session')) { throw new \LogicException('PHP extension "session" is required.'); } $options += [ 'cache_limiter' => '', 'cache_expire' => 0, 'use_cookies' => 1, 'lazy_write' => 1, 'use_strict_mode' => 1, ]; session_register_shutdown(); $this->setMetadataBag($metaBag); $this->setOptions($options); $this->setSaveHandler($handler); } /** * Gets the save handler instance. * * @return AbstractProxy|\SessionHandlerInterface */ public function getSaveHandler() { return $this->saveHandler; } /** * {@inheritdoc} */ public function start() { if ($this->started) { return true; } if (PHP_SESSION_ACTIVE === session_status()) { throw new \RuntimeException('Failed to start the session: already started by PHP.'); } if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line)) { throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line)); } // ok to try and start the session if (!session_start()) { throw new \RuntimeException('Failed to start the session.'); } if (null !== $this->emulateSameSite) { $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id()); if (null !== $originalCookie) { header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false); } } $this->loadSession(); return true; } /** * {@inheritdoc} */ public function getId() { return $this->saveHandler->getId(); } /** * {@inheritdoc} */ public function setId($id) { $this->saveHandler->setId($id); } /** * {@inheritdoc} */ public function getName() { return $this->saveHandler->getName(); } /** * {@inheritdoc} */ public function setName($name) { $this->saveHandler->setName($name); } /** * {@inheritdoc} */ public function regenerate($destroy = false, $lifetime = null) { // Cannot regenerate the session ID for non-active sessions. if (PHP_SESSION_ACTIVE !== session_status()) { return false; } if (headers_sent()) { return false; } if (null !== $lifetime) { ini_set('session.cookie_lifetime', $lifetime); } if ($destroy) { $this->metadataBag->stampNew(); } $isRegenerated = session_regenerate_id($destroy); // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it. // @see https://bugs.php.net/70013 $this->loadSession(); if (null !== $this->emulateSameSite) { $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id()); if (null !== $originalCookie) { header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false); } } return $isRegenerated; } /** * {@inheritdoc} */ public function save() { // Store a copy so we can restore the bags in case the session was not left empty $session = $_SESSION; foreach ($this->bags as $bag) { if (empty($_SESSION[$key = $bag->getStorageKey()])) { unset($_SESSION[$key]); } } if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) { unset($_SESSION[$key]); } // Register error handler to add information about the current save handler $previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) { if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) { $handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler; $msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler)); } return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false; }); try { session_write_close(); } finally { restore_error_handler(); // Restore only if not empty if ($_SESSION) { $_SESSION = $session; } } $this->closed = true; $this->started = false; } /** * {@inheritdoc} */ public function clear() { // clear out the bags foreach ($this->bags as $bag) { $bag->clear(); } // clear out the session $_SESSION = []; // reconnect the bags to the session $this->loadSession(); } /** * {@inheritdoc} */ public function registerBag(SessionBagInterface $bag) { if ($this->started) { throw new \LogicException('Cannot register a bag when the session is already started.'); } $this->bags[$bag->getName()] = $bag; } /** * {@inheritdoc} */ public function getBag($name) { if (!isset($this->bags[$name])) { throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name)); } if (!$this->started && $this->saveHandler->isActive()) { $this->loadSession(); } elseif (!$this->started) { $this->start(); } return $this->bags[$name]; } public function setMetadataBag(MetadataBag $metaBag = null) { if (null === $metaBag) { $metaBag = new MetadataBag(); } $this->metadataBag = $metaBag; } /** * Gets the MetadataBag. * * @return MetadataBag */ public function getMetadataBag() { return $this->metadataBag; } /** * {@inheritdoc} */ public function isStarted() { return $this->started; } /** * Sets session.* ini variables. * * For convenience we omit 'session.' from the beginning of the keys. * Explicitly ignores other ini keys. * * @param array $options Session ini directives [key => value] * * @see https://php.net/session.configuration */ public function setOptions(array $options) { if (headers_sent() || PHP_SESSION_ACTIVE === session_status()) { return; } $validOptions = array_flip([ 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly', 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite', 'gc_divisor', 'gc_maxlifetime', 'gc_probability', 'lazy_write', 'name', 'referer_check', 'serialize_handler', 'use_strict_mode', 'use_cookies', 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags', 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags', ]); foreach ($options as $key => $value) { if (isset($validOptions[$key])) { if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) { // PHP < 7.3 does not support same_site cookies. We will emulate it in // the start() method instead. $this->emulateSameSite = $value; continue; } ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value); } } } /** * Registers session save handler as a PHP session handler. * * To use internal PHP session save handlers, override this method using ini_set with * session.save_handler and session.save_path e.g. * * ini_set('session.save_handler', 'files'); * ini_set('session.save_path', '/tmp'); * * or pass in a \SessionHandler instance which configures session.save_handler in the * constructor, for a template see NativeFileSessionHandler. * * @see https://php.net/session-set-save-handler * @see https://php.net/sessionhandlerinterface * @see https://php.net/sessionhandler * * @param AbstractProxy|\SessionHandlerInterface|null $saveHandler * * @throws \InvalidArgumentException */ public function setSaveHandler($saveHandler = null) { if (!$saveHandler instanceof AbstractProxy && !$saveHandler instanceof \SessionHandlerInterface && null !== $saveHandler) { throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.'); } // Wrap $saveHandler in proxy and prevent double wrapping of proxy if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { $saveHandler = new SessionHandlerProxy($saveHandler); } elseif (!$saveHandler instanceof AbstractProxy) { $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler())); } $this->saveHandler = $saveHandler; if (headers_sent() || PHP_SESSION_ACTIVE === session_status()) { return; } if ($this->saveHandler instanceof SessionHandlerProxy) { session_set_save_handler($this->saveHandler, false); } } /** * Load the session with attributes. * * After starting the session, PHP retrieves the session from whatever handlers * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()). * PHP takes the return value from the read() handler, unserializes it * and populates $_SESSION with the result automatically. */ protected function loadSession(array &$session = null) { if (null === $session) { $session = &$_SESSION; } $bags = array_merge($this->bags, [$this->metadataBag]); foreach ($bags as $bag) { $key = $bag->getStorageKey(); $session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : []; $bag->initialize($session[$key]); } $this->started = true; $this->closed = false; } } PK!=S*c#Storage/PhpBridgeSessionStorage.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session\Storage; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; /** * Allows session to be started by PHP and managed by Symfony. * * @author Drak */ class PhpBridgeSessionStorage extends NativeSessionStorage { /** * @param AbstractProxy|\SessionHandlerInterface|null $handler */ public function __construct($handler = null, MetadataBag $metaBag = null) { if (!\extension_loaded('session')) { throw new \LogicException('PHP extension "session" is required.'); } $this->setMetadataBag($metaBag); $this->setSaveHandler($handler); } /** * {@inheritdoc} */ public function start() { if ($this->started) { return true; } $this->loadSession(); return true; } /** * {@inheritdoc} */ public function clear() { // clear out the bags and nothing else that may be set // since the purpose of this driver is to share a handler foreach ($this->bags as $bag) { $bag->clear(); } // reconnect the bags to the session $this->loadSession(); } } PK! \]]SessionUtils.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session; /** * Session utility functions. * * @author Nicolas Grekas * @author Rémon van de Kamp * * @internal */ final class SessionUtils { /** * Finds the session header amongst the headers that are to be sent, removes it, and returns * it so the caller can process it further. */ public static function popSessionCookie(string $sessionName, string $sessionId): ?string { $sessionCookie = null; $sessionCookiePrefix = sprintf(' %s=', urlencode($sessionName)); $sessionCookieWithId = sprintf('%s%s;', $sessionCookiePrefix, urlencode($sessionId)); $otherCookies = []; foreach (headers_list() as $h) { if (0 !== stripos($h, 'Set-Cookie:')) { continue; } if (11 === strpos($h, $sessionCookiePrefix, 11)) { $sessionCookie = $h; if (11 !== strpos($h, $sessionCookieWithId, 11)) { $otherCookies[] = $h; } } else { $otherCookies[] = $h; } } if (null === $sessionCookie) { return null; } header_remove('Set-Cookie'); foreach ($otherCookies as $h) { header($h, false); } return $sessionCookie; } } PK!n$QQSessionBagInterface.phpnuIw * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpFoundation\Session; /** * Session Bag store. * * @author Drak */ interface SessionBagInterface { /** * Gets this bag's name. * * @return string */ public function getName(); /** * Initializes the Bag. */ public function initialize(array &$array); /** * Gets the storage key for this bag. * * @return string */ public function getStorageKey(); /** * Clears out data from bag. * * @return mixed Whatever data was contained */ public function clear(); } PK!;mSessionHandlerInterface.phpnu[PK!9\== Session_driver.phpnu[PK!lDM''$drivers/Session_memcached_driver.phpnu[PK!!-Cookie/Identity/cdesc-Identity.rinu[PK!WĹCookie/Identity/encode-i.rinu[PK!|%>Cookie/new-c.rinu[PK!\Cookie/cdesc-Cookie.rinu[PK!Ԗ**@Cookie/set_session-i.rinu[PK!XDCookie/digest_match%3f-i.rinu[PK!{2 Cookie/Base64/JSON/cdesc-JSON.rinu[PK!M>\Cookie/Base64/JSON/decode-i.rinu[PK!gCookie/Base64/JSON/encode-i.rinu[PK!}}rCookie/Base64/cdesc-Base64.rinu[PK!opa<Cookie/Base64/decode-i.rinu[PK!W!<Cookie/Base64/Marshal/decode-i.rinu[PK!&.&MCookie/Base64/Marshal/cdesc-Marshal.rinu[PK!!PCookie/Base64/Marshal/encode-i.rinu[PK!90!aCookie/Base64/ZipJSON/decode-i.rinu[PK!X4LL&rCookie/Base64/ZipJSON/cdesc-ZipJSON.rinu[PK!!Cookie/Base64/ZipJSON/encode-i.rinu[PK!T %Cookie/Base64/encode-i.rinu[PK!}ȁ%Cookie/extract_session_id-i.rinu[PK!zCCookie/get_session-i.rinu[PK! |))Cookie/destroy_session-i.rinu[PK!P+%YCookie/generate_hmac-i.rinu[PK!ACookie/coder-i.rinu[PK!]| Pool/mutex-i.rinu[PK!By w Pool/new-c.rinu[PK!Ttf Pool/set_session-i.rinu[PK!0V Pool/with_lock-i.rinu[PK!wl| Pool/pool-i.rinu[PK!uIZTPool/cdesc-Pool.rinu[PK! |Ӵ8Pool/generate_sid-i.rinu[PK!侺2Pool/get_session-i.rinu[PK!B91Pool/destroy_session-i.rinu[PK!)0Lcdesc-Session.rinu[PK!‡9QQ\Abstract/cdesc-Abstract.rinu[PK!DB Abstract/SessionHash/delete-i.rinu[PK!rv  Abstract/SessionHash/values-i.rinu[PK!_J_Abstract/SessionHash/clear-i.rinu[PK!`"'Abstract/SessionHash/empty%3f-i.rinu[PK!'M!9Abstract/SessionHash/replace-i.rinu[PK!jQ$PAbstract/SessionHash/has_key%3f-i.rinu[PK!Z#Abstract/SessionHash/exists%3f-i.rinu[PK!>((* Abstract/SessionHash/load_for_read%21-i.rinu[PK!Q6"Abstract/SessionHash/new-c.rinu[PK!`e H#Abstract/SessionHash/key%3f-i.rinu[PK!-W$Abstract/SessionHash/store-i.rinu[PK!^f$n%Abstract/SessionHash/include%3f-i.rinu[PK!L&Abstract/SessionHash/set-c.rinu[PK!ZHo--('Abstract/SessionHash/stringify_keys-i.rinu[PK!h!")Abstract/SessionHash/destroy-i.rinu[PK!Th 5*Abstract/SessionHash/%5b%5d-i.rinu[PK!gAOY+Abstract/SessionHash/fetch-i.rinu[PK!c(i,Abstract/SessionHash/each-i.rinu[PK![y-Abstract/SessionHash/find-c.rinu[PK!*5!.Abstract/SessionHash/options-i.rinu[PK!!/Abstract/SessionHash/to_hash-i.rinu[PK!L)0Abstract/SessionHash/keys-i.rinu[PK!ZO"1Abstract/SessionHash/merge%21-i.rinu[PK!Z**+=Abstract/SessionHash/load_for_write%21-i.rinu[PK!#?Abstract/SessionHash/%5b%5d%3d-i.rinu[PK!!B@Abstract/ID/force_options%3f-i.rinu[PK!T?Q66)AAbstract/ID/forced_session_update%3f-i.rinu[PK!PP?CAbstract/ID/commit_session-i.rinu[PK!k)z(($EAbstract/ID/security_matches%3f-i.rinu[PK!D  "ZGAbstract/ID/commit_session%3f-i.rinu[PK!␍0IAbstract/ID/new-c.rinu[PK!^??JAbstract/ID/cdesc-ID.rinu[PK!.{{#@SAbstract/ID/current_session_id-i.rinu[PK!]UAbstract/ID/context-i.rinu[PK!VAbstract/ID/set_session-i.rinu[PK!98{{wXAbstract/ID/session_class-i.rinu[PK!RUI@ZAbstract/ID/set_cookie-i.rinu[PK!0nn#k\Abstract/ID/extract_session_id-i.rinu[PK!",^Abstract/ID/loaded_session%3f-i.rinu[PK!%+  _Abstract/ID/generate_sid-i.rinu[PK!QʻaAbstract/ID/get_session-i.rinu[PK! UdAbstract/ID/default_options-i.rinu[PK!` deAbstract/ID/destroy_session-i.rinu[PK!4rgAbstract/ID/load_session-i.rinu[PK! ĂtiAbstract/ID/initialize_sid-i.rinu[PK!%' jAbstract/ID/prepare_session-i.rinu[PK!odd"lAbstract/ID/session_exists%3f-i.rinu[PK!k{ֶnAbstract/ID/call-i.rinu[PK!ЧoAbstract/ID/key-i.rinu[PK!ĠypMemcache/mutex-i.rinu[PK!'M#`qMemcache/new-c.rinu[PK!^`g[rMemcache/set_session-i.rinu[PK!Q`пsMemcache/with_lock-i.rinu[PK!0ActMemcache/pool-i.rinu[PK!؋KmuMemcache/generate_sid-i.rinu[PK!asvMemcache/get_session-i.rinu[PK!CpF~wMemcache/destroy_session-i.rinu[PK!B88xMemcache/cdesc-Memcache.rinu[PK!E%| | '~PHP8SessionWrapper.phpnu[PK!2 OldSessionWrapper.phpnu[PK!I#bCI_Session_driver_interface.phpnu[PK!Pެ*SessionUpdateTimestampHandlerInterface.phpnu[PK!Xአnn  Memcache.htmlnu[PK!*+IQaQa Pool.htmlnu[PK!B3V5V5 swAbstract.htmlnu[PK!#+FAFACookie/Base64/Marshal.htmlnu[PK!xvkBBCookie/Base64/JSON.htmlnu[PK!qqCqC1Cookie/Base64/ZipJSON.htmlnu[PK!l:@@QuCookie/Base64.htmlnu[PK!?r==|Cookie/Identity.htmlnu[PK!W#yGyG aCookie.htmlnu[PK!ڙtPSPS<Abstract/ID.htmlnu[PK!ȖAbstract/SessionHash.htmlnu[PK!sR## Aupdate-i.yamlnu[PK!ķ  BMemoryStore/update-i.yamlnu[PK!"MDMemoryStore/cdesc-MemoryStore.yamlnu[PK!IcNGMemoryStore/delete-i.yamlnu[PK!u>::HMemoryStore/restore-i.yamlnu[PK!JMemoryStore/new-c.yamlnu[PK!uTLMemoryStore/close-i.yamlnu[PK!Ue M%5b%5d-i.yamlnu[PK!7{22Ncdesc-Session.yamlnu[PK! 4idelete-i.yamlnu[PK! jFileStore/update-i.yamlnu[PK!  SlFileStore/delete-i.yamlnu[PK!f__mFileStore/restore-i.yamlnu[PK!$XKoFileStore/new-c.yamlnu[PK!.7<uFileStore/close-i.yamlnu[PK!i  vFileStore/cdesc-FileStore.yamlnu[PK!a* * *znew-c.yamlnu[PK!Rcreate_new_id-i.yamlnu[PK!9п?? close-i.yamlnu[PK!rև NoSession/cdesc-NoSession.yamlnu[PK!乤T.%5b%5d%3d-i.yamlnu[PK!G yymSessionBagProxy.phpnuIwPK!, !$)Attribute/NamespacedAttributeBag.phpnuIwPK!T T xAttribute/AttributeBag.phpnuIwPK!O UO#Attribute/AttributeBagInterface.phpnuIwPK!2  Flash/FlashBag.phpnuIwPK!}0c c Flash/AutoExpireFlashBag.phpnuIwPK!v$Flash/FlashBagInterface.phpnuIwPK!1|SessionInterface.phpnuIwPK!tb"Storage/MockFileSessionStorage.phpnuIwPK!5#Storage/SessionStorageInterface.phpnuIwPK! )GStorage/Handler/MongoDbSessionHandler.phpnuIwPK!I' (IStorage/Handler/StrictSessionHandler.phpnuIwPK!~*$Storage/Handler/AbstractSessionHandler.phpnuIwPK!ˋ&7Storage/Handler/NullSessionHandler.phpnuIwPK!?d[)r<Storage/Handler/SessionHandlerFactory.phpnuIwPK!qE~  'KStorage/Handler/RedisSessionHandler.phpnuIwPK! +YStorage/Handler/MigratingSessionHandler.phpnuIwPK!%fStorage/Handler/PdoSessionHandler.phpnuIwPK! ds< < +XStorage/Handler/MemcachedSessionHandler.phpnuIwPK!+8##,Storage/Handler/NativeFileSessionHandler.phpnuIwPK!M#n Storage/MockArraySessionStorage.phpnuIwPK!RkrXXStorage/MetadataBag.phpnuIwPK!+ %E,Storage/Proxy/SessionHandlerProxy.phpnuIwPK!ȧ- - 5Storage/Proxy/AbstractProxy.phpnuIwPK!%-7-7 ?Storage/NativeSessionStorage.phpnuIwPK!=S*c#vStorage/PhpBridgeSessionStorage.phpnuIwPK! \]]|SessionUtils.phpnuIwPK!n$QQ_SessionBagInterface.phpnuIwPK>