diff options
Diffstat (limited to 'core/classes/session')
| -rw-r--r-- | core/classes/session/DatabaseSession.php | 138 | ||||
| -rw-r--r-- | core/classes/session/DefaultSession.php | 42 | ||||
| -rw-r--r-- | core/classes/session/FilesSession.php | 138 | ||||
| -rw-r--r-- | core/classes/session/SessionHandlerInterface.php | 169 |
4 files changed, 487 insertions, 0 deletions
diff --git a/core/classes/session/DatabaseSession.php b/core/classes/session/DatabaseSession.php new file mode 100644 index 0000000..aa31b62 --- /dev/null +++ b/core/classes/session/DatabaseSession.php @@ -0,0 +1,138 @@ +<?php + +// class DatabaseSession implements Session_interface +class DatabaseSession implements SessionHandlerInterface +{ + /** PROPERTIES + * ------------------------------------------------------------------------- + */ + + private $db; // the database object + + // private $db_driver; // the database driver + + + /** METHODS + * ------------------------------------------------------------------------- + */ + + /** __construct + * set database connection + * set session handler to overide default session + * start session + */ + public function __construct() + { + // Prepare the Database object + $this->db = Registry::use('database'); + + // Set handler to overide SESSION + session_set_save_handler( + array($this, "open"), + array($this, "close"), + array($this, "read"), + array($this, "write"), + array($this, "destroy"), + array($this, "gc") + ); + + // Start the session + session_name(SESSION_NAME); + session_start(); + } + + // public function open(string $path, string $name) : bool + // NOTE: $path and $name are unsued + public function open() : bool + { + if ($this->db) { + return true; + } + else { + // database connection may be closed from another class + // (ex. from some user class); in this case... + // create a new database connection, then recheck. + $conn = new Database(); + // $this->db = $conn->connect(); + + if ($conn) { + $this->db = $conn; + return true; + } + } + return false; + } + + + public function close() : bool + { + // depricated: the database object is shared (don;t clode it!) + // // Close the database connection + // if ($this->db = null) { return true; } + // else return false; + + return true; + } + + + public function read(string $id) : string|false + { + $exist = $this->db->runQuery( + "SELECT data FROM sessions WHERE id = :id", + [':id', $id] + ); + + if (($exist === false) || (count($exist) == 0)) { + return false; + + } else { + $data = $exist[0]; + } + + if (is_null($row['data'])) { + return ''; + } + return $row['data']; + } + + + public function write(string $id, string $data) : bool + { + // Create timestamp + $access = time(); + $check = $this->db->runQuery( + "REPLACE INTO sessions VALUES (:id, :access, :data)", + [':id' => $id, ':access' => $access, ':data' => $data ] + ); + return ($check == false) ? false : true; + } + + + public function destroy(string $sassionID) : bool + { + $check = $this->db->runQuery( + 'DELETE FROM sessions WHERE id = :id', + [':id' => $sassionID] + ); + return ($check == false) ? false : true; + } + + + public function gc(int $max) + { + // Calculate what is to be deemed old + $old = time() - $max; + $check = $this->db->runQuery( + 'DELETE FROM sessions WHERE access < :old', + [':old' => $old] + ); + return ($check == false) ? false : 1; + // check garbage-collector probability to run + // echo "probability: ". ini_get("session.gc_probability") ." / ". ini_get("session.gc_divisor") . ", ttl: ". ini_get("session.gc_maxlifetime"); die(); + + } + + # callable $create_sid = ?, + # callable $validate_sid = ?, + # callable $update_timestamp = ? +} diff --git a/core/classes/session/DefaultSession.php b/core/classes/session/DefaultSession.php new file mode 100644 index 0000000..079ab9d --- /dev/null +++ b/core/classes/session/DefaultSession.php @@ -0,0 +1,42 @@ +<?php + +/** DefauleSession + * -- + * + * is a dummy session hanlder that wraps the + * php's default session engine implementation; + * + */ +class DefaultSession implements SessionHandlerInterface { + + public function __construct() + { + // Start the session + // (the default way) + // and let the session* functions + // do what they know + session_name(SESSION_NAME); + session_start(); + } + + #[\ReturnTypeWillChange] + public function open(string $path, string $name) {} + + #[ReturnTypeWillChange] + public function close() {} + + #[ReturnTypeWillChange] + public function read(string $id) { + + } + + #[ReturnTypeWillChange] + public function write(string $id, string $data) {} + + #[ReturnTypeWillChange] + public function destroy(string $id) {} + + #[ReturnTypeWillChange] + public function gc(int $max_lifetime) {} + +}
\ No newline at end of file diff --git a/core/classes/session/FilesSession.php b/core/classes/session/FilesSession.php new file mode 100644 index 0000000..a3c8437 --- /dev/null +++ b/core/classes/session/FilesSession.php @@ -0,0 +1,138 @@ +<?php + +/** FileSessionHandler + * is a custom File-based Session Handler + * + * Could be useful when implemented along with data-cryptography, + * otherwise php's default session handler (which is also file-based) + * seems to be the obvious way to go; + * + * NOTE: + * if shared sessions accros an array of servers is needed, + * a database-session handler is probably the best choice. + * + * TODO: + * implement cryptography + */ +class FileSessionHandler implements SessionHandlerInterface +{ + /** The filesystem instance. */ + protected $sessionName; + + /** The path where all sessions should be stored. */ + protected $path; + + /** The number of minutes the session should be valid. */ + protected $minutes; + + /** + * Create a new file driven session-handler instance. + * + * @param string $path + * @param int $minutes + * @return void + */ + public function __construct() + { + // values comming from configuration + $this->sessionName = SESSION_NAME; + $this->sess_filename = FILESESSION_PATH; + $this->minutes = $minutes; + + // Set handler to overide SESSION + session_set_save_handler( + array($this, "open"), + array($this, "close"), + array($this, "read"), + array($this, "write"), + array($this, "destroy"), + array($this, "gc") + ); + + // Start the session + session_name($this->sessionName); + session_start(); + } + + /** open + * set filename; + * no need to touch the filesystem yet + * return true (always) + */ + public function open($savePath, $sessionName): bool + { + $this->sess_filename = $this->$path .'/'. $sessionName; + return true; + } + + /** close + * nothing needs to be closed; + * return true (always) + */ + public function close(): bool + { + return true; + } + + /** read + * chech if file exists; if not return false + * read data + */ + public function read($sessionId): string|false + { + if (!file_exists($filename) || !is_readable($filename)) return false; + $data = file_get_contents($filename); + + return @unserialize($data); + } + + /** write + * data serielized already by php's internal session engine + */ + public function write($sessionId, $data): bool + { + $h = fopen($filename, 'w'); + if (fwrite($h, serialize($data)) === false) { + throw new Exception('Could not write session data'); + return false; + } + fclose($h); + + return true; + } + + /** + * {@inheritdoc} + * + * @return bool + */ + public function destroy($sassionID): bool + { + unlink($this->sassionID); + + return true; + } + + /** + * {@inheritdoc} + * + * @return int + */ + public function gc($lifetime) : int + { + $files = Finder::create() + ->in($this->path) + ->files() + ->ignoreDotFiles(true) + ->date('<= now - '. $lifetime .' seconds'); + + $deletedSessions = 0; + + foreach ($files as $file) { + $this->files->delete($file->getRealPath()); + $deletedSessions++; + } + + return $deletedSessions; + } +} diff --git a/core/classes/session/SessionHandlerInterface.php b/core/classes/session/SessionHandlerInterface.php new file mode 100644 index 0000000..cf6efac --- /dev/null +++ b/core/classes/session/SessionHandlerInterface.php @@ -0,0 +1,169 @@ +<?php + +/** SessionHandlerInterface + * + * as of php's documentation (check manual) + * https://www.php.net/manual/en/class.sessionhandlerinterface.php + */ +interface SessionHandlerInterface +{ + + /* Methods + *-------------------------------------------------------------------------- + */ + + public function open(string $path, string $name) : bool; + + public function close() : bool; + + public function read(string $id) : string|false; + + public function write(string $id, string $data) : bool; + + public function destroy(string $id) : bool; + + public function gc(int $max_lifetime) : int|false; + +} + +/** TODO: (thoughts) + * ... for a brand-new session implementation + * + * In most cases, the session data does not require persistence. + * The session information could be cached to improve performance + * + * So (in theory) we could create a two-dimension session mechanism + * + * + a fast dimension + * : could be file-based (using local FS [php's default?] or redis/memcached) + * + * + a shared dimension + * : implemented in a shared database + * + * Posible algo: + * ... (check later on this comment-block: session life-cycle ) + * + * open : + * close : on both + * read : check Local; if not exist check database; set session_id($id) + * write : on both + * destroy : on both + * gc : on server + * + * when regenerating session_id, keep database informed about the new session_id + * when closing, keep database informed about the session end-of-life. + * + * a session timeout on the local leg will trigger a confirm-session-from-db + * + * garbage collector on the db should remove expired sessions of some X seconds + * and earier (X needs to be determined by practice/expirience/tries) + * + * (+) in order to keep tracking of the session between multiple servers through + * the (shared) database, a unique secret-between-the-servers id can be used + * + * so... + * + * #1 + * -> someone visits website through server A + * -> server A creates a new session-id for the visitor and a shared-session-id + * -> saves both into database + * + * later... + * + * #2 + * -> the same device is connected (via loadbalancer) into server B + * -> server B don't have the session-id (send by the client) but gets it from db + * (is session-id do not exist on the db, then this is a new session) + * + * = now both server A and B have the same session-id localy + * + * later... + * + * #3 + * -> some server (let's say B) regenerates the visitors session-id + * -> if later the visitor falls into server A, the A will retrieve the new + * session id through the #2 scenario + * + * this way + * + any server can update/regenerate the visitor's session-id + * + the visitor can be served from both servers randomly + * + all session variables exist on the database (always) + * + all session variables exist on all servers synced-on-demand + * + * + * stucture of session: + * --- + * - shared-session-id: (secret + persistent); exposed between servers **primary + * - session-id: (may change/regenerate/update); exposed to the client **indexed + * - data: serialized array + * - creation_timestamp: + * - last_touched_timestamp: + * - expiration_timestamp: should point to the future or session has expired + * - csrf_token: + * - JWT_token + * - AES-key (this way private data can be kept in browser) + * + * + * what will kept on browser/client (via cookie) + * --- + * - session-name => session-id + * - user info => AES_ectypted(serialized[t=>csfr_token, u=> user_id, s=>SIGNATURE]) + * + * + * Writable file-system shall change + * (local writable file-system tree) + * --- + * /html/storage + * | + * |-- cache : query-caches + * | + * `-- session : FS\sessions + * | + * `-- indexes : share-session-id indexes + * + * + * + * NOTE: Session life-cycle + * + * session_start() * firsts time + * --- + * ::open(path,PHPSESSID) -> (session_id not exist) -> false + * ::create_sid -> '123def' + * ::read('123def') ?-or/and- ::close() + * + * + * session_start() * next times + * --- + * ::open(path, PHPSESSID) -> (session_id exist) -> true + * ::read('123def') -> return data -> will fill $_SESSION[*] + * + * + * $_SESSION['foo'] = 'bar'; + * --- + * ::write('123def', 'foo|s:3:"bar";') -> ['foo' => "bar"] + * ::close() + * + * + * session_regenerate_id(); + * --- + * ::create_sid() -> def123 + * + * + * session_reset() + * --- + * ::open() + * ::read('def123') + * + * + * session_write_close() + * --- + * ::write('123def', 'foo|s:3:"bar";') + * ::close() + * + * + * session_destroy() + * --- + * ::destroy('def123') + * ::close() + * + */ |
