基于PHP数据库的会话数据

时间:2014-10-28 17:59:02

标签: php mysql session

我正在尝试使用数据库存储细节的会话处理程序。

有一点是,如果当前正在使用会话数据,那么我不希望它们被加载(直到第一个脚本完成并更新数据)。

我有一个基本类来做这个看起来很有效,下面有一个小测试脚本: -

<?php

class MySessionHandler implements SessionHandlerInterface
{

    private $db = null;

    public function open($savePath, $sessionName)
    { 
        $host = 'localhost';
        $user = 'root';
        $pass = '';
        $name = 'em_sessions';
        $this->db = new mysqli($host, $user, $pass, $name);
        return true;
    }

    public function close()
    {
        return true;
    }

    public function read($session_key)
    {
        $this->db->autocommit(FALSE);

        if($stmt = $this->db->prepare("SELECT data FROM sessions WHERE session_key = ? FOR UPDATE"))
        {
            $stmt->bind_param('s', $session_key);
            $stmt->execute();
            echo $stmt->error." - ".$stmt->errno."<br />";
            $retry = 0;
            while ($stmt->errno == 1213 and $retry++ < 20)
            {
                usleep(50);
                $stmt->execute();
                echo $stmt->error." - ".$stmt->errno."<br />";
            }
            $stmt->store_result();
            $stmt->bind_result($data);
            $stmt->fetch();
        }
        else
        {
            echo "Unable to prepare statement ".$this->db->error."<br />";
        }
        return $data;
    }

    public function write($session_key, $data)
    {

        $time = time();
        if($stmt = $this->db->prepare("INSERT INTO sessions (set_time, data, session_key) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE set_time=VALUES(set_time), data=VALUES(data)"))
        {
            echo "Written(".time().")<br />";
            $stmt->bind_param('iss', $time, $data, $session_key);
            $stmt->execute();

            $this->db->commit();
        }
        else
        {
            echo "Unable to prepare statement ".$this->db->error."<br />";
        }
        return true;
    }

    public function destroy($session_key)
    {
        if($stmt = $this->db->prepare("DELETE FROM sessions WHERE session_key = ?"))
        {
            $stmt->bind_param('s', $session_key);
            $stmt->execute();
        }
        else
        {
            echo "Unable to prepare statement ".$this->db->error."<br />";
        }
        return true;
    }

    public function gc($maxlifetime)
    {
        if($stmt = $this->db->prepare("DELETE FROM sessions WHERE set_time < ?"))
        {
            $old = time() - $max;
            $stmt->bind_param('s', $old);
            $stmt->execute();
        }
        else
        {
            echo "Unable to prepare statement ".$this->db->error."<br />";
        }
        return true;
    }

}

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();
echo session_id()."<br />";

echo "<a href='http://localhost/TestArea/session_test.php'>Do Me</a><br />";

if (!array_key_exists('cnt', $_SESSION))
{
    echo "No session cnt found so initialising<br />";
    $_SESSION['cnt'] = 0;
}

echo "Page called ".++$_SESSION['cnt']." times (".time().")<br />";

sleep(2);

session_write_close();

sleep(2);

echo "Ended(".time().")<br />";

基本类使用FOR UPDATE选择会话详细信息以锁定行(会话ID是唯一键),关闭自动提交。当要保存会话详细信息时(即,脚本结束或调用session_write_close()时)将调用一个方法来使用ON DUPLICATE KEY UPDATE插入会话详细信息,然后执行提交以释放锁定。

测试脚本的想法是它加载会话数据,等待几秒钟,调用session_write_close()(它应该触发会话详细信息写入表,并释放锁定以便下次尝试抓住表格然后等待几秒钟再结束剧本。

这只是为了测试会话是否在需要时被锁定,然后使用相同的会话ID释放下一个脚本。

然而,在测试这个时我有一个奇怪的问题。如果我一次触发这个脚本的六个运行(页面上有一个链接 - 只需用鼠标中键点击该链接六次),那么第一个运行正常。但是在第一个脚本完成之前不会触发第二个脚本(即第二次出现的开始时间与第一次出现的结束时间相同)。这让我觉得不对。

第3次出现将更快开始。它们的开始时间将是先前出现的会话详细信息写入表中的时间。这就是我所期望的。

我的困惑在于我为什么要在脚本的第二次和后续出现之间产生差异。我真正使用后续处理的方式会很好,但第一个可能是一个更大的问题;也是最常发生的情况。

我可能错过了一些简单的事情。

会话数据表如下: -

CREATE TABLE IF NOT EXISTS `sessions` (
  `set_time` int(11) NOT NULL,
  `session_key` char(32) NOT NULL,
  `data` longtext NOT NULL,
  PRIMARY KEY (`session_key`),
  KEY `set_time` (`set_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

0 个答案:

没有答案