Memorizzare i dati di sessione in un database con SessionHandlerInterface e PDO

Oltre all'utilizzo dei file come sistema di memorizzazione dei dati in sessione, PHP offre anche la possibilità di utilizzare altri sistemi come ad esempio un database.

Pubblicato da ,
Ultima modifica

Oltre all'utilizzo dei file, esistono diversi modi per memorizzare i dati di sessione, ad esempio con Redis, Memcache, ma è anche possibile salvare i dati in un database, in questo articolo vedremo come implementare l'interfaccia SessionHandlerInterface per gestire le sessioni e memorizzare i dati in un database usando PDO ( PHP Data Objects ), l'interfaccia SessionHandlerInterface è la seguente

Codice PHP

interface SessionHandlerInterface {

/* Methods */
public close(): bool
public destroy(string $id): bool
public gc(int $max_lifetime): int|false
public open(string $path, string $name): bool
public read(string $id): string|false
public write(string $id, string $data): bool

}

ha cinque metodi open e close rispettivamente per aprire e chiudere una sessione, destroy per distruggere una sessione, read e write rispettivamente per leggere e scrivere una sessione, e infine gc per effettuare una pulizia di tutte le vecchie sessioni.

In questo esempio creeremo una classe PDOSessionHandler che implementa l'interfaccia SessionHandlerInterface con tutti i metodi che abbiamo visto in precedenza ( in particolare si presuppone che si stia eseguendo una versione di php `8.0.0` o successive ), la tabella in cui andremo a memorizzare i dati di sessione sarà così composta

Codice SQL

CREATE TABLE session ( 
`id` varchar(256) NOT NULL, 
`name` varchar(256) NOT NULL,
`value` longtext, 
`last_update` int(11) NOT NULL, 
PRIMARY KEY (`id`,`name`) ) ENGINE = INNODB;

`name` è il nome del cookie che viene generato, di default è `PHPSESSID` ( se non specificato diversamente ), `id` è l'identificativo di sessione cioè il valore del cookie, `value` sono i dati che vengono salvati nella sessione, `last_update` è il timestamp dell'ultimo aggiornamento della sessione.

Questa è la classe PDOSessionHandler

Codice PHP

<?php

class PDOSessionHandler implements SessionHandlerInterface
 {
     private $pdo;
     private $sessionName;
 
     public function __construct(PDO $pdo){
         $this->pdo = $pdo;
     }
 
     public function open($savePath, $sessionName): bool{
         $this->sessionName = $sessionName;
         return true;
     }
 
 
     public function close(): bool{
         $this->pdo = null;
         return true;
     }
 
 
     public function read($id){
         $sql = "SELECT value FROM session WHERE name = :name AND id = :id";
         $sth = $this->pdo->prepare($sql);
         $sth->execute([":name" => $this->sessionName, ":id" => $id]);
         $result = $sth->fetch(PDO::FETCH_ASSOC);
         return !isset($result["value"]) ? "" : $result["value"];
     }
 
     public function write($id, $value): bool{
         $sql = "SELECT value FROM session WHERE name = :name AND id = :id";
         $sth = $this->pdo->prepare($sql);
         $sth->execute([":name" => $this->sessionName, ":id" => $id]);
 
         if (count($sth->fetchAll()) == 0) {
             $sql =
                 "INSERT INTO session (id, name, value, last_update) values (:id, :name, :value, :last_update)";
         } else {
             $sql =
                 "UPDATE session SET value = :value, last_update = :last_update WHERE id = :id AND name = :name";
         }
 
         $sth = $this->pdo->prepare($sql);
 
         return $sth->execute([
             ":id" => $id,
             ":name" => $this->sessionName,
             ":value" => $value,
             ":last_update" => strtotime(date("Y-m-d H:i:s")),
         ]);
     }
 
     public function destroy($id): bool{
         $sql = "DELETE FROM session WHERE name = :name and id = :id";
         $sth = $this->pdo->prepare($sql);
         return $sth->execute([":name" => $this->sessionName, ":id" => $id]);
     }
 
     public function gc($maxlifetime){
         $sql = "DELETE FROM session WHERE last_update < :lifetime";
         $sth = $this->pdo->prepare($sql);
         return $sth->execute([
             ":lifetime" => strtotime(date("Y-m-d H:i:s")) - $maxlifetime,
         ]);
     }
 }

Per utilizzare il session handler PDOSessionHandler è necessario utilizzare la funzione session_set_save_handler() che accetta come parametro di ingresso una classe che implementa l'interfaccia SessionHandlerInterface, quindi nel nostro caso procederemo nel seguente modo

Codice PHP

<?php

$username = "username";
$password = "password";
$databasename = "databasename";

$pdo = new PDO(
    "mysql:dbname=$databasename;host=localhost;",
    $username,
    $password
);
session_set_save_handler(new PDOSessionHandler($pdo));

in questo caso sto usando un database di tipo MySQL.

Per iniziare a usare questo gestore di dati in sessione basta eseguire la funzione session_start()

Codice PHP

<?php

session_start();

in questo modo, a ogni modifica o lettura dell'array globale $_SESSION comporterà una modifica dei dati in sessione momorizzati nella tabella session che abbimao visto in precedenza, questa modalità può essere usata per condividere i dati in sessione tra più server.

Code on GitHub