اول از همه بهتره یه کلاس کوچک برای ارتباط با دیتابیس ایجاد کنیم. من ترجیح میدم با PDO این کار رو انجام بدم:
class DB
{
/** @var PDO $con */
private static $con = null;
const DB = 'session_db';
const HOST = 'localhost';
const PASS = '';
const USER = 'root';
public function __construct()
{
throw new Exception('Cannot create an object from this class.');
}
public static function connect()
{
$dsn = 'mysql:dbname=' . self::DB . ';host=' . self::HOST . ';charset=utf8';
try {
self::$con = new PDO($dsn, self::USER, self::PASS);
} catch (Exception $e) {
die('Connection failed: ' . $e->getMessage());
}
self::execute('SET NAMES 'utf8'');
}
public static function execute($query)
{
if (!self::$con) {
self::Connect();
}
return self::$con->exec($query);
}
public static function arrayQuery($query, $params = null)
{
if (!self::$con) {
self::connect();
}
$data = [];
$statement = self::$con->prepare($query);
if ($statement->execute($params)) {
foreach ($statement->fetchAll(PDO::FETCH_OBJ) as $row) {
$data[] = $row;
}
}
return $data;
}
public static function query($query, $params = null)
{
if (!self::$con) {
self::connect();
}
$statement = self::$con->prepare($query);
if ($statement->execute($params)) {
return $statement->rowCount();
}
return -1;
}
}
حالا باید کلاس مدیریت سشن خودمون رو بنویسیم. برای این کار باید کلاس ما حتماً رابط SessionHandlerInterface رو پیادهسازی کنه. کد این کلاس رو میگذارم:
class DbSessionHandler implements SessionHandlerInterface
{
public $sessionTable = 'session_table';
public $autoCreateSessionTable = true;
public $expireTime = 1200; // in Seconds
private function createTable()
{
DB::query('START TRANSACTION;');
DB::query("
CREATE TABLE `{$this->sessionTable}` (
`id` char(32) NOT NULL,
`data` longblob NOT NULL,
`expire` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
");
DB::query("ALTER TABLE `{$this->sessionTable}` ADD PRIMARY KEY (`id`);");
DB::query('COMMIT;');
}
private function deleteExpired()
{
return DB::query("DELETE FROM `{$this->sessionTable}` WHERE (`expire`<:expire)", [':expire' => time()]);
}
public function close()
{
return true;
}
public function destroy($session_id)
{
DB::query("DELETE FROM `{$this->sessionTable}` WHERE (`id`=:id)", [':id' => $session_id]);
return true;
}
public function gc($maxlifetime)
{
$this->deleteExpired();
return true;
}
public function open($save_path, $name)
{
if ($this->autoCreateSessionTable) {
if ($this->deleteExpired() < 0) {
$this->createTable();
}
}
return true;
}
public function read($session_id)
{
$data = DB::arrayQuery("SELECT `data` FROM `{$this->sessionTable}` WHERE (`id`=:id AND `expire`>=:expire)", [':id' => $session_id, ':expire' => time()]);
return (count($data) > 0 ? base64_decode($data[0]->data) : '');
}
public function write($session_id, $session_data)
{
return DB::query("REPLACE INTO `{$this->sessionTable}` VALUES (:id, :data, :expire)", [':id' => $session_id, ':data' => base64_encode($session_data), ':expire' => time() + $this->expireTime]) > 0;
}
}
همونطور که میبینین، این کد خیلی شبیه کدی هست که قبلاً نوشتیم و نکتهی کلیدی قسمت implements SessionHandlerInterface هست و پیادهسازی متدهای close و destroy و gc و open و read و write هست.
حالا تنها کاری که باقی مونده، استفاده از این کلاس هست. کافیه به تابع session_set_save_handler خود PHP، یه شئ از این کلاسی که ساختین رو پاس بدین و بعد مثل حالتهای عادی session_start() رو صدا بزنین:
require_once 'DB.php';
require_once 'DbSessionHandler.php';
session_set_save_handler(new DbSessionHandler());
session_start();
$_SESSION['name'] = 'ncis.ir';
با یه نگاه به دیتابیس متوجه میشین که جدول session_table درصورتی که وجود نداشته باشه، ساخته میشه و مقادیر سشن توی این جدول ثبت میشن. واضحه که اسامی جدول و دیتابیس و مدتزمان اعتبار هر متغیر سشن رو میتونین براساس نیازتون تغییر بدین. ازطرفی من متدهای اضافه که توی تاپیک قبلی بود و برای کارهای دیگه مثل آمار کاربران فعال و... نوشته بودیم رو حذف کردم و درصورت تمایل خودتون میتونین دوباره اونها رو اضافه کنین.