Descobri ao longo de várias horas de depuração que os artigos referenciados encontrados em várias pesquisas do Google, bem como um subconjunto significativo de respostas do Stack Overflow, como aqui , aqui e aqui todos fornecem informações inválidas ou desatualizadas.
Coisas que podem causar problemas [críticos] ao salvar dados de sessão em um banco de dados:
-
Enquanto todos os exemplos online afirmam que você pode "preencher" osession_set_save_handler
, nenhum deles afirma que você também deve definir aregister_shutdown_function('session_write_close')
também (referência ).
-
Vários guias (mais antigos) referem-se a uma estrutura de banco de dados SQL desatualizada e não ser usado. A estrutura do banco de dados necessária para salvar os dados da sessão no banco de dados é:id
/access
/data
. É isso. não há necessidade de várias colunas extras de timestamp, como vi em alguns "guias" e exemplos.
- Vários dos guias mais antigos também têm sintaxe desatualizada do MySQL, como
DELETE * FROM ...
- Vários dos guias mais antigos também têm sintaxe desatualizada do MySQL, como
-
A aula [feita na minha pergunta] deve implementar aSessionHandlerInterface
. Eu vi guias (referenciados acima) que fornecem a implementação desessionHandler
que não é uma interface adequada. Talvez as versões anteriores do PHP tivessem um método ligeiramente diferente (provavelmente <5.4).
-
Os métodos de classe de sessão devem retorne os valores definidos pelo manual do PHP. Novamente, provavelmente herdado do PHP pré-5.4, mas dois guias que li afirmaram queclass->open
retorna a linha a ser lida, enquanto o informações do manual do PHP que ele precisa retornartrue
oufalse
só.
-
Esta é a causa do meu problema original :eu estava usando nomes de sessão personalizados (na verdade, id's como nomes de sessão e ids de sessão são a mesma coisa! ) conforme esta muito boa postagem do StackOverflow e isso estava gerando um nome de sessão com 128 caracteres. Como o nome da sessão é a única chave que precisa ser quebrada para comprometer uma sessão e assumir o controle com um seqüestro de sessão então um nome/id mais longo é uma coisa muito boa.
- Mas isso causou um problema porque o MySQL estava fatiando silenciosamente o ID da sessão reduzido para apenas 32 caracteres em vez de 128, por isso nunca foi capaz de encontrar os dados da sessão no banco de dados. Este foi um problema completamente silencioso (talvez devido à minha classe de conexão de banco de dados não lançar avisos de tais coisas). Mas este é o único a observar. Se você tiver algum problema com a recuperação de sessões de um banco de dados, verifique primeiro se o arquivo completo O ID da sessão pode ser armazenado no campo fornecido.
Então, com tudo isso fora do caminho, há alguns detalhes extras para adicionar também:
A página de manual do PHP (link acima) mostra uma pilha inadequada de linhas para um objeto de classe:
Considerando que funciona tão bem se você colocar isso no construtor da classe:
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();
}
...
}
Isso significa que para iniciar uma sessão em sua página de saída, tudo o que você precisa é:
<?php
require "path/to/sessionhandler.class.php";
new MySessionHandler();
//Bang session has been setup and started and works
Para referência, a classe completa de comunicação de sessão é a seguinte, isso funciona com PHP 5.6 (e provavelmente 7, mas ainda não testado em 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();
}
}
Uso:Como mostrado logo acima do texto do código da classe.