我知道这已经有一段时间了,我希望你在发布后找到了答案,但为了后人的利益,我会添加我的解决方案。
对session_generate_id() 的调用将导致session_id() 的值发生变化:
<?php
$before = session_id();
session_regenerate_id();
$after = session_id();
var_dump($before == $after); // outputs false
这个问题对我来说很明显,因为在会话写入处理程序中我正在这样做(当然没有这样的虚假方法名称):
<?php
class MySQLHandler
{
function read($id)
{
$row = $this->doSelectSql($id);
if ($row) {
$this->foundSessionDuringRead = true;
}
// snip
}
function write($id, $data)
{
if ($this->foundSessionDuringRead) {
$this->doUpdateSql($id, $data);
}
else {
$this->doInsertSql($id, $data);
}
}
}
如果从未调用过 session_regenerate_id(),则 write() 方法可以正常工作。但是,如果调用它,write() 的 $id 参数与传递给 read() 的 $id 不同,因此更新将找不到任何具有新 $id 的记录,因为它们从未被插入。
有些人建议使用 MySQL 的“REPLACE INTO”语法,但这会删除并替换行,如果您想要创建日期列,这会造成极大的破坏。我解决此问题的方法是保留传递给read 的会话ID,然后在写入期间使用传递给read 的ID 作为密钥更新数据库中的会话ID:
<?php
class MySQLHandler
{
function read($id)
{
$row = $this->doSelectSql($id);
if ($row) {
$this->rowSessionId = $id;
}
// snip
}
function write($id, $data)
{
if ($this->rowSessionId) {
$stmt = $this->pdo->prepare("UPDATE session SET session_id=:id, data=:data WHERE session_id=:rowSessionId AND session_name=:sessionName");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':rowSessionId', $this->rowSessionId);
$stmt->bindValue(':data', $data);
$stmt->bindValue(':sessionName', $this->sessionName);
$stmt->execute();
}
else {
$this->doInsertSql($id, $data);
}
}
}