В хода на няколко часа отстраняване на грешки открих, че посочените статии са намерени в многобройни търсения в Google, както и значителна подмножество от отговори на Stack Overflow, като тук , тук и тук всички предоставят невалидна или остаряла информация.
Неща, които могат да причинят [критични] проблеми със запазването на данни за сесията в база данни:
-
Докато всички примери онлайн посочват, че можете да "попълните"
session_set_save_handler
, нито един от тях не посочва, че трябва да зададете иregister_shutdown_function('session_write_close')
също (справка ). -
Няколко (по-стари) ръководства се отнасят до остаряла структура на SQL база данни и трябва да не използван. Структурата на базата данни, която ви е необходима за запазване на данни за сесията в базата данни е:
id
/access
/data
. Това е. няма нужда от различни допълнителни колони за времеви отпечатъци, както видях в няколко „ръководства“ и примери.- Няколко от по-старите ръководства също имат остарял синтаксис на MySQL, като
DELETE * FROM ...
- Няколко от по-старите ръководства също имат остарял синтаксис на MySQL, като
-
Класът [направен в моя въпрос] трябва внедрява
SessionHandlerInterface
. Виждал съм ръководства (посочени по-горе), които дават реализацията наsessionHandler
което не е подходящ интерфейс. Може би предишните версии на PHP са имали малко по-различен метод (вероятно <5.4). -
Методите на класа на сесията трябва върнете стойностите, посочени в ръководството за PHP. Отново, вероятно наследено от PHP преди 5.4, но две ръководства, които прочетох, гласят, че
class->open
връща реда за четене, докато PHP ръчното състояние че трябва да върнеtrue
илиfalse
само. -
Това е причината за първоначалния ми проблем :Използвах персонализирани имена на сесии (всъщност идентификаторите като имена на сесии и идентификаторите на сесии са едно и също нещо! ) според тази много добра публикация в StackOverflow и това генерираше име на сесия, което беше дълго 128 знака. Тъй като името на сесията е единственият ключ, който трябва да бъде разбит, за да се компрометира сесия и да се поеме с отвличане на сесия тогава по-дълго име/идентификатор е много добро нещо.
- Но това предизвика проблем, защото MySQL безшумно изрязваше идентификатора на сесията до само 32 знака вместо 128, така че никога не успя да намери данните за сесията в базата данни. Това беше напълно тих проблем (може би поради моят клас за връзка с база данни, който не изпраща предупреждения за такива неща). Но това е този, за който трябва да внимавате. Ако имате проблеми с извличането на сесиите от база данни, първата проверка е дали пълната идентификаторът на сесията може да бъде съхранен в предоставеното поле.
Така че като изключим всичко това, има и някои допълнителни подробности за добавяне:
Страницата с ръководство на PHP (свързана по-горе) показва неподходяща купчина редове за обект на клас:
Докато работи също толкова добре, ако поставите това в конструктора на класа:
class MySessionHandler implements SessionHandlerInterface {
private $database = null;
public function __construct(){
$this->database = new Database(whatever);
// 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")
);
register_shutdown_function('session_write_close');
session_start();
}
...
}
Това означава, че за да започнете сесия на вашата изходна страница, всичко, от което се нуждаете, е:
<?php
require "path/to/sessionhandler.class.php";
new MySessionHandler();
//Bang session has been setup and started and works
За справка пълният комуникационен клас Session е както следва, това работи с PHP 5.6 (и вероятно 7, но все още не е тестван на 7)
<?php
/***
* Created by PhpStorm.
***/
class MySessionHandler implements SessionHandlerInterface {
private $database = null;
public function __construct($sessionDBconnectionUrl){
/***
* Just setting up my own database connection. Use yours as you need.
***/
require_once "class.database.include.php";
$this->database = new DatabaseObject($sessionDBconnectionUrl);
// 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")
);
register_shutdown_function('session_write_close');
session_start();
}
/**
* Open
*/
public function open($savepath, $id){
// If successful
$this->database->getSelect("SELECT `data` FROM sessions WHERE id = ? LIMIT 1",$id,TRUE);
if($this->database->selectRowsFoundCounter() == 1){
// Return True
return true;
}
// Return False
return false;
}
/**
* Read
*/
public function read($id)
{
// Set query
$readRow = $this->database->getSelect('SELECT `data` FROM sessions WHERE id = ? LIMIT 1', $id,TRUE);
if ($this->database->selectRowsFoundCounter() > 0) {
return $readRow['data'];
} else {
return '';
}
}
/**
* Write
*/
public function write($id, $data)
{
// Create time stamp
$access = time();
// Set query
$dataReplace[0] = $id;
$dataReplace[1] = $access;
$dataReplace[2] = $data;
if ($this->database->noReturnQuery('REPLACE INTO sessions(id,access,`data`) VALUES (?, ?, ?)', $dataReplace)) {
return true;
} else {
return false;
}
}
/**
* Destroy
*/
public function destroy($id)
{
// Set query
if ($this->database->noReturnQuery('DELETE FROM sessions WHERE id = ? LIMIT 1', $id)) {
return true;
} else {
return false;
}
}
/**
* Close
*/
public function close(){
// Close the database connection
if($this->database->dbiLink->close){
// Return True
return true;
}
// Return False
return false;
}
/**
* Garbage Collection
*/
public function gc($max)
{
// Calculate what is to be deemed old
$old = time() - $max;
if ($this->database->noReturnQuery('DELETE FROM sessions WHERE access < ?', $old)) {
return true;
} else {
return false;
}
}
public function __destruct()
{
$this->close();
}
}
Употреба:Както е показано точно над текста на кода на класа.