当前位置: 首页 > 面试题库 >

如何将PHP会话数据保存到数据库而不是文件系统中?

云宜人
2023-03-14
问题内容

我有两个网站,一个是TLS,一个不是,这两个都是针对同一客户端的,但是我需要这些网站彼此(并且只能彼此)共享 用户订单帐户
等的通用数据。

通常可以使用$_SESSION数据完成此操作,但是我显然不能在其他站点上使用它们,而且我发现我可以将会话数据存储在数据库(MySQL)中,而不是文件系统中。

我已经四处搜寻,发现此有用的指南以及此较旧但 有用的指南。我还发现了该指南,该指南具有最新的MySQL。

我已经编写了一个接口类,但是它只能部分起作用,它将会话数据存储在数据库中,但不会检索它。我还使用了PHP手册中建议的方法。

我的MySQL (复制自上述前两个链接):

CREATE TABLE `sessions` (
  `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
  `access` int(10) NOT NULL,
  `data` text COLLATE utf8_unicode_ci NOT NULL,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDb DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

请注意: 在向我展示我的接口类之前,请知道Db连接使用我自己定制的接口,并且它本身可以完美地工作。

$sessionDBconnectionUrl为我保持从主要网站内容的独立数据库会话包含会话数据库连接的详细信息。

我的界面类 (基于以上所有链接)

<?php
/***
 * Created by PhpStorm.
 ***/
class HafSessionHandler implements SessionHandler {
    private $database = null;

    public function __construct($sessionDBconnectionUrl){

        if(!empty($sessionDBconnectionUrl) && file_exists($_SERVER['DOCUMENT_ROOT'].$sessionDBconnectionUrl)) {
            require_once "class.dataBase.php";
            // Instantiate new Database object
            $this->database = new Database($sessionDBconnectionUrl);
        }
        else {
            error_log("Session could not initialise class.");
        }

    }

    /**
     * Open
     */
    public function open($savepath, $id){
         $openRow = $this->database->getSelect("SELECT `data` FROM sessions WHERE id = ? LIMIT 1",$id);
    if($this->database->selectRowsFoundCounter() == 1){
        // Return True
        return $openRow['data'];
        }
    else {
        // Return False
        return ' ';
    }
    /**
     * 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 {
            error_log("could not read session id ".$id);
            return '';
        }
    }

    /**
     * Write
     */
    public function write($id, $data)
    {
        $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 = ? ', $id)) {
            return TRUE;
        } else {

            return FALSE;
        }
    }
    /**
     * Close
     */
    public function close(){
        // Close the database connection
        // If successful
        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;

        // Set query
        if ($this->database->noReturnQuery('DELETE * FROM sessions WHERE access < ?', $old)) {
            return TRUE;
        } else {
            return FALSE;
        }
    }

    public function __destruct()
    {
        $this->close();
    }

}

我的测试页 (从头开始!)

<?php
require "class.sessionHandler.inc.php";
$HSH = new HafSessionHandler("connection.session.dbxlink.php");
session_set_save_handler( $HSH, TRUE );
session_start();

print "<p>Hello this is an index page</p>";
$_SESSION['horses'] = "treesx3";
$_SESSION['tiespan'] = (int)$_SESSION['tiespan']+7;

print "<p>There should be some session data in the database now. <a href='index3.php'>link</a></p>";
var_dump($_SESSION);


exit;

问题:

我运行的测试页可以将数据保存到数据库中,但是它们似乎无法检索到数据,

我已启用错误日志记录,并且未报告任何PHP错误。没有报告严重的MySQL错误。

为什么不起作用?


问题答案:

在经过数小时的调试过程中,我发现在众多Google搜索中找到的参考文章以及大量的Stack
Overflow答案(例如here,here和here)都提供了无效或过时的信息。

在将会话数据保存到数据库中可能导致[严重]问题的事情:

  • 尽管所有在线示例都说明您可以“填充” session_set_save_handler,但没有一个示例说明您也必须设置它register_shutdown_function('session_write_close')(参考)。

  • 一些(旧的)导游指的是过时的SQL数据库结构,应该 不会 被使用。将会话数据保存到数据库所需的数据库结构是:id/ access/ data。而已。正如我在一些“指南”和示例中看到的那样,不需要各种额外的时间戳列。

    • 一些较旧的指南也已经过时的MySQL语法,例如 DELETE * FROM ...
    • 类[我的问题作出]必须 实现SessionHandlerInterface。我已经看到了指南(上面已引用),这些指南给出的实现方式sessionHandler不合适。也许以前版本的PHP的方法稍有不同(可能小于5.4)。
  • 会话类方法 必须 返回PHP手册中列出的值。同样,可能是从PHP 5.4之前的版本继承的,但是我阅读的两个指南指出class->open返回要读取的行,而PHP手册指出需要返回truefalse仅返回行 。

  • 这是导致我的原始问题的原因 :根据这个非常好的StackOverflow帖子,我使用的是自定义会话名称(实际上,会话名称和会话ID 都是相同的! ),并且生成的会话名称长度为128个字符。由于会话名称是唯一的密钥,需要破解才能破坏会话并接管会话劫持,因此较长的名称/ ID是一件好事。

    • 但是,这引起了一个问题,因为 MySQL默默地 将会话 ID切成 32个字符而不是128个字符,因此它永远无法在数据库中找到会话数据。这是一个完全沉默的问题(可能是由于我的数据库连接类未引发此类警告)。但这是要提防的。如果从数据库检索会话有任何问题,请首先检查 完整的 会话ID是否可以存储在提供的字段中。

因此,除了所有这些之外,还需要添加一些额外的细节:

PHP手册页(上面链接)显示了一个类对象不合适的行:

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();

如果将其放在类构造函数中,效果也一样:

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();
    }

}

用法:如类代码文本上方所示。



 类似资料:
  • 我收到的流数据()希望保存在S3中(基本上,对于这个问题,我希望将输出保存在哪里并不重要,但我只是为了以防万一而提到它)。 是否可以将每个(这些是JSON字符串)数据保存到JSON文件中,而不是文件夹中?我以为必须使用这个技巧,但它没有。

  • 问题内容: 我有这个项目: 导入文件 连接到SQL Server数据库 将所有数据转移到数据库中 文本文件按选项卡划分为四个字段,例如数据库。 我已经完成了使用富文本框并将所有数据保存在字符串中的第一步。我的想法是将字符串拆分为每行并将其保存在数组中,然后:如何拆分每一行,以便可以正确保存字段?如何将SQL Server上的数据库连接到C#上的项目? 问题答案: 让我们一次解决这一步骤… 获取数据

  • 问题内容: 我的一位客户要求为成千上万种不同格式(例如pdf,doc,docx等)的文档提供文档管理系统。我的问题是在数据库或文件系统中存储此文件的最佳方法是什么?两种方法之间如何轻松保护文档? 快速检索文件是关键要求。 我正在使用mysql如果有帮助 问候。 问题答案: 您可能希望将其直接存储到文件系统中。 使用文件系统时,请注意: 机密性: 将文档放在Apache文档根目录之外。然后,您的PH

  • 我们正在上运行spark 2.3.0。以下“”不是空的,大小适中: 以下代码可以很好地将写入: 然而,使用相同的代码写入本地< code>parquet或< code>csv文件最终会得到空结果: 我们可以看到它失败的原因: 因此,没有正在写入镶木地板文件。 我已经对< code>csv和< code>parquet以及两个不同的< code>EMR服务器尝试了大约二十次:在所有情况下都表现出相同

  • 我不明白为什么Javascript提取API顽固地拒绝保留我的PHP会话。下面是一个最小的测试: 我发现了几个类似的问题/答案,但没有一个有用。建议的解决方案是为凭证选项提供“包含”或“同一站点”,但它们都不起作用。 我知道我可以传递会话ID,但如果可能,我希望避免它。 谢谢你的帮助

  • XML 服务 实体 存储库 我有一个错误: 创建在类路径资源[org/springframework/boot/autoconfigure/orm/jpa/hibernatejbaconfiguration.class]中定义的名为“entityManagerFactory”的bean时出错:初始化方法调用失败;嵌套的异常是javax。坚持不懈PersistenceException:[Persi