我有一个任务是用PHP实现我的自定义会话处理程序,其中来自每个会话的数据保存在JSON文本文件中的指定目录中.
以下是代码(它是文档中的代码和其中的用户注释的组合):
class CustomSessionHandler implements SessionHandlerInterface
{
//this field contains folder, where all sessions files will be stored
private string $savePath;
//according to docs
public function close(): bool
{
return true;
}
//delete file of session with specified ID
public function destroy(string $id): bool
{
$sessionPath = $this->savePath . DIRECTORY_SEPARATOR . "sess_" . $id . ".json";
if (file_exists($sessionPath)) {
unlink($sessionPath);
}
return true;
}
//according to docs
public function gc(int $max_lifetime): int|false
{
foreach (glob($this->savePath . DIRECTORY_SEPARATOR . "sess_*" . ".json") as $file) {
if (filemtime($file) + $max_lifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
//generally, 'if' doesn't make any sense because save directory in my
//study project will always exist
public function open(string $path, string $name): bool
{
$this->savePath = $path;
if (!is_dir($path)) {
mkdir($path);
}
return true;
}
//I think the problem is here...
public function read(string $id): string
{
$sessionPath = $this->savePath . DIRECTORY_SEPARATOR . "sess_" . $id . ".json";
if (!file_exists($sessionPath)) {
file_put_contents($sessionPath, "");
chmod($sessionPath, 0777);
}
return file_get_contents($sessionPath);
}
//create JSON string from serialized session string
public function write(string $id, string $data): bool
{
$rawData = [];
try {
$rawData = self::unserialize_php($data);
} catch (Exception $e) {
echo $e->getMessage();
}
$jsonData = json_encode($rawData);
return !(file_put_contents($this->savePath . DIRECTORY_SEPARATOR . 'sess_' . $id . ".json", $jsonData) === false);
}
/**
* @throws Exception
*/
//this code is from comment section from docs and it works fine
private static function unserialize_php($sessionData): array
{
$returnData = [];
$offset = 0;
while ($offset < strlen($sessionData)) {
if (!str_contains(substr($sessionData, $offset), "|")) {
throw new Exception("invalid data, remaining: " . substr($sessionData, $offset));
}
$pos = strpos($sessionData, "|", $offset);
$num = $pos - $offset;
$varName = substr($sessionData, $offset, $num);
$offset += $num + 1;
$data = unserialize(substr($sessionData, $offset));
$returnData[$varName] = $data;
$offset += strlen(serialize($data));
}
return $returnData;
}
}
我创建了一个身份验证系统.在负责判断用户凭据的名为login.php的文件的开头,有以下代码:
$handler = new CustomSessionHandler();
session_set_save_handler($handler);
session_start([
'save_path' => '/var/www/phpcourse.loc/hometask_3/sessions',
]);
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true) {
//welcome.php is the page, where user just see 'Hi, {username}' and can logout
header('Location: welcome.php');
exit;
}
问题是会话文件是在不应该发生的时候创建和删除的.
正因为如此,我无法登录,我有一个警告:
Warning:Session_Start():无法解码会话对象.会话已在/var/www/phpcourse.loc/hometask_3/login.php中的第12行被销毁
接下来,如果用户凭据正确,则设置$_SESSION变量:
if (password_verify($password, $hashed_password)) {
$handler = new CustomSessionHandler();
session_set_save_handler($handler);
session_start([
'save_path' => '/var/www/phpcourse.loc/hometask_3/sessions',
]);
$_SESSION['logged_in'] = true;
$_SESSION['id'] = $id;
$_SESSION['username'] = $username;
header('Location: welcome.php');
}
如果我调试这段代码并完成每个步骤,将会发生以下情况:
-
在第一次下载登录页面时,创建的会话文件完全为空
-
按下登录按钮后,在从CustomSessionHandler类的Read()函数返回值之后,会话文件被销毁.它发生在上述代码中的第一个SESSION_START()中.
-
当代码到达第二个SESSION_START()时,当所有凭据都正确并且下一个页面(欢迎.php)打开时,将再次创建会话文件,并且所有变量都将以JSON格式写入其中.
但是,当我以与文件login.php相同的方式启动脚本开头的Session时,在Read()之后,文件再次被销毁.我又回到了登录页面.
怎么一回事?
Upate:我的CustomSessionHandler似乎有问题,因为如果我只在每个脚本的开头加上简单的session_start(),一切都是正常的.
也许includes有问题?下面是文件,其中调用了SESSION_START().我不会将文件放在用户注销时会话被 destruct 的位置.
login.php个
<?php
use hometask_3\DBConnection;
use hometask_3\CustomSessionHandler;
require_once 'classes/DBConnection.php';
require_once 'classes/CustomSessionHandler.php';
$handler = new CustomSessionHandler();
session_set_save_handler($handler);
session_start([
'save_path' => '/var/www/phpcourse.loc/hometask_3/sessions',
]);
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true) {
header('Location: welcome.php');
exit;
}
//next step is form validation, I just leave code where session
//variables are used
if (password_verify($password, $hashed_password)) {
$_SESSION['logged_in'] = true;
$_SESSION['id'] = $id;
$_SESSION['username'] = $username;
header('Location: welcome.php');
}
在重新加载登录页面后,再次显示有关会话对象已被 destruct 的警告.
welcome.php个
<?php
use hometask_3\CustomSessionHandler;
require_once 'classes/CustomSessionHandler.php';
$handler = new CustomSessionHandler();
session_set_save_handler($handler);
session_start([
'save_path' => '/var/www/phpcourse.loc/hometask_3/sessions',
]);
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
header('Location: login.php');
exit;
}
include 'view/welcomeForm.html.php';
我仍然不能理解,当我在欢迎.php文件中调用SESSION_START()时,为什么调用会话处理程序中的方法Destroy().