summaryrefslogtreecommitdiff
path: root/classes/session
diff options
context:
space:
mode:
Diffstat (limited to 'classes/session')
-rw-r--r--classes/session/DatabaseSession.php138
-rw-r--r--classes/session/DefaultSession.php35
-rw-r--r--classes/session/FilesSession.php142
-rw-r--r--classes/session/SessionHandlerInterface.php169
4 files changed, 484 insertions, 0 deletions
diff --git a/classes/session/DatabaseSession.php b/classes/session/DatabaseSession.php
new file mode 100644
index 0000000..aa31b62
--- /dev/null
+++ b/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/classes/session/DefaultSession.php b/classes/session/DefaultSession.php
new file mode 100644
index 0000000..d3099a5
--- /dev/null
+++ b/classes/session/DefaultSession.php
@@ -0,0 +1,35 @@
+<?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
+ 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/classes/session/FilesSession.php b/classes/session/FilesSession.php
new file mode 100644
index 0000000..8762776
--- /dev/null
+++ b/classes/session/FilesSession.php
@@ -0,0 +1,142 @@
+<?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->sess_filename = $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(SESSION_NAME);
+ 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;
+ return file_get_contents($filename);
+
+ # if ($this->files->isFile($path = $this->path.'/'.$sessionId) &&
+ # $this->files->lastModified($path) >= Carbon::now()->subMinutes($this->minutes)->getTimestamp()) {
+ # return $this->files->sharedGet($path);
+ # }
+
+ # depricated? return '';
+ }
+
+ /** write
+ * data serielized already by php's internal session engine
+ */
+ public function write($sessionId, $data): bool
+ {
+ $h = fopen($filename, 'w');
+ if (fwrite($h,$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/classes/session/SessionHandlerInterface.php b/classes/session/SessionHandlerInterface.php
new file mode 100644
index 0000000..cf6efac
--- /dev/null
+++ b/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()
+ *
+ */