There are some good reasons not to store session data in files. Performance might be one issue, with security as another issue. An alternative way to store session information is to use databases. For this, you just need a database table (we named it data_table
) with three columns:
- Column
id
(primary key) is of typeVARCHAR(32)
and contains the session ID. - Column
data
is of typeTEXT
and contains the session data. - Column
access
is of typeVARCHAR(14)
and contains the time stamp of the most recent access to the session information.
SQL code for creating the session table:
CREATE TABLE `data_table` ( `id` VARCHAR(32) NOT NULL , `data` TEXT NOT NULL , `access` VARCHAR(14) NOT NULL , PRIMARY KEY (`id`(32)), INDEX (`access`(14)) );
Table structure preview in PhpMyAdmin:
It doesn’t matter which database is used. Our code uses PDO which supports MySQL, MariaDB, CUBRID, MS SQL Server, Oracle, Firebird, PostgreSQL, SQLite, and some other RDBMS.
The PHP function session_set_save_handler()
is used to set the user-level session storage functions for storing and retrieving data associated with a session. This function has two prototypes.
Whatever prototype you use, you need to create custom functions/methods for all relevant six session operations PHP uses internally:
- Opening a session
- Closing a session
- Reading a session variable
- Writing a session variable
- Destroying a session
- Cleaning up (garbage collection, for example removing old session data from the database)
session_set_save_handler(): callable prototype syntax
<?php //Syntax # 1 session_set_save_handler( callable $open, callable $close, callable $read, callable $write, callable $destroy, callable $gc, callable $create_sid = ?, callable $validate_sid = ?, callable $update_timestamp = ? ): bool
session_set_save_handler(): sessionhandler prototype syntax
<?php //Syntax # 2 session_set_save_handler( object $sessionhandler, bool $register_shutdown = true ): bool
For all these six operations, the following listings contain code that reads and writes session data from and to a MySQL/MariaDB data source. You need PHP and must set the connection parameters (server, username, password, etc.) appropriately. Then, you include the code from this listing using require_once and use sessions as usual. In the background, PHP saves all session information in the database, not in the file system.
1. Using callable functions and PDO to store session data
Creating sessions.php:
<?php //sessions.php $dbh = null; $db_table = ''; function open($savePath, $sessionName){ global $dbh, $db_table; $db_name = 'sessions'; $db_user = 'root'; $db_pass = ''; $db_host = 'localhost'; $db_port = 3306; $db_table = 'data_table'; $dsn = "mysql:host=$db_host;port=$db_port;dbname=$db_name"; try { $dbh = new PDO($dsn, $db_user, $db_pass); } catch (PDOException $e) { echo "Error: $e->getMessage()"; exit; } return true; } function close(){ global $dbh; $dbh = null; return true; } function read($id){ global $dbh, $db_table; $q = $dbh->prepare("SELECT data FROM $db_table WHERE id=?"); $q->bindParam(1, $id); if ( $q->execute() ) { if ($q->rowCount() == 0) return ''; return $q->fetchColumn(); } return false; } function write($id, $data){ global $dbh, $db_table; $ts = date('YmdHis'); $q = $dbh->prepare("REPLACE INTO $db_table VALUES(?, ?, ?)"); $q->bindParam(1, $id); $q->bindParam(2, $data); $q->bindParam(3, $ts); return $q->execute(); } function destroy($id){ global $dbh, $db_table; $q = $dbh->prepare("DELETE FROM $db_table WHERE id=?"); $q->bindParam(1, $id); return $q->execute(); } function gc($maxlifetime){ global $dbh, $db_table; $ts = date('YmdHis', time() - $maxlifetime); $q = $dbh->prepare("DELETE FROM $db_table WHERE access < ?"); $q->bindParam(1, $ts); $q->execute(); if ( $q->execute() ) return $q->rowCount(); return false; } session_set_save_handler('open','close','read','write','destroy','gc');
Including the code from this listing using require_once
and use sessions as usual:
<?php //example.php require_once 'sessions.php'; session_start(); $_SESSION['username'] = 'Admin'; $_SESSION['location'] = 'US';
2. Implementing SessionHandlerInterface to store session data
Creating sessions_class.php:
<?php //sessions_class.php class DbSessionClass implements SessionHandlerInterface { private $dbh; private $db_host = 'localhost'; private $db_port = 3306; private $db_name = 'sessions'; private $db_user = 'root'; private $db_pass = ''; private $db_table = 'data_table'; public function open($savePath, $sessionName): bool { $dsn = "mysql:host=$this->db_host;port=$this->db_port;dbname=$this->db_name"; try { $this->dbh = new PDO($dsn, $this->db_user, $this->db_pass); } catch (PDOException $e) { echo 'Error!: ' . $e->getMessage(); exit; } return true; } public function close(): bool { $this->dbh = null; return true; } public function read($id):string|false { $q = $this->dbh->prepare("SELECT data FROM $this->db_table WHERE id=?"); $q->bindParam(1, $id); if ( $q->execute() ) { if ($q->rowCount() == 0) return ''; return $q->fetchColumn(); } return false; } public function write($id, $data): bool { $ts = date('YmdHis'); $q = $this->dbh->prepare("REPLACE INTO $this->db_table VALUES(?, ?, ?)"); $q->bindParam(1, $id); $q->bindParam(2, $data); $q->bindParam(3, $ts); return $q->execute(); } public function destroy($id): bool { $q = $this->dbh->prepare("DELETE FROM $this->db_table WHERE id=?"); $q->bindParam(1, $id); return $q->execute(); } public function gc($maxlifetime): int|false { $ts = date('YmdHis', time() - $maxlifetime); $q = $this->dbh->prepare("DELETE FROM $this->db_table WHERE access < ?"); $q->bindParam(1, $ts); if ( $q->execute() ) return $q->rowCount(); return false; } } $handler = new DbSessionClass(); session_set_save_handler($handler, true);
The following figure shows the contents of the database table after data has been written into it:
<?php //example.php require_once 'sessions_class.php'; session_start(); $_SESSION['username'] = 'Admin'; $_SESSION['location'] = 'US';
Cookies and Sessions: