【问题标题】:Session data deleted on session_start() after page redirect页面重定向后在 session_start() 上删除的会话数据
【发布时间】:2012-08-05 12:13:18
【问题描述】:

经过大量调试后,问题似乎(令人尴尬)出在我的数据库会话代码中,而不是典型的会话问题。你可以看到我的答案here - 谢谢


我知道这可能是类似问题的重复(例如onetwothree),但尽管遵循了似乎是最佳实践的方法,但我仍然遇到问题。

当使用 session_set_save_handler() 来使用我的数据库会话类时,会话数据在从 session1.php 重定向后在 session2.php 开始时被清除。

我的观察概述:

  • 数据在session1.php中正确保存到数据库中
  • session2.php 中的 session_start() 数据丢失
  • 重定向之后,在 session2.php 中调用 session_start() 之前,数据仍在数据库中
  • 会话 ID 保持不变,并存储在 cookie 中,该 cookie 在请求标头中正确发送回服务器
  • 使用 PHP 的默认 Session 处理可以正常工作

注意:

  • exit() 在 header() 之后使用
  • 输出前每一页的session_start()

我犯了一个愚蠢的错字吗?犯了一个愚蠢的错误?或者这是一个奇怪的怪癖?

提前感谢您提供的任何帮助。

这是代码(在修复此问题时提取到测试文件中):

session1.php

<?php

require_once('session.php');

session_start();  

$_SESSION['KEY'] = 'VALUE PHPSESSID: ' . session_id();

session_write_close();   
header('Location: session2.php');
exit;

session2.php

<?php

require_once('session.php');

session_start();

// Nothing?
var_dump( $_SESSION );

session.php

<?php

define( "DB_HOST", 'localhost' );
define( "DB_USER", '******' );
define( "DB_PWD", '******' );
define( "DB_NAME", '******' );

require_once('class/DatabaseSessionHandler.php');

// Use the DatabaseSessionHandler class to handle sessions
$session_handler = new DatabaseSessionHandler;
// Set up the handler above as the default session handler
session_set_save_handler(
    array($session_handler, 'open'),
    array($session_handler, 'close'),
    array($session_handler, 'read'),
    array($session_handler, 'write'),
    array($session_handler, 'destroy'),
    array($session_handler, 'gc')
);

DatabaseSessionHandler.php

<?php

class DatabaseSessionHandler
{

    protected $connection;
    protected $session_life_time;

    public function __construct()
    {
        // Ensure that everything is closed correctly as 
        // per warning on http://uk3.php.net/session_set_save_handler
        register_shutdown_function( 'session_write_close' );
    }

    public function open( $save_path, $session_name )
    {
        $this->connection = new mysqli( DB_HOST, DB_USER, DB_PWD, DB_NAME );
        $this->session_life_time = get_cfg_var( "session.gc_maxlifetime" );

        if ( $this->connection->connect_error )
            return false;

        return true;
    }

    public function close()
    {
        $this->connection->close();
        return true;
    }

    public function read( $session_id )
    {
        $data = '';

        $statement = $this->connection->prepare( "SELECT `session_data` 
                                                  FROM `session` 
                                                  WHERE `session_id` = ? " );
        $statement->bind_param( "s", $session_id );
        $statement->execute();
        $statement->bind_result( $data );

        return (string) $data;
    }

    public function write( $session_id, $session_data )
    {
        $expiry_time = time() + $this->session_life_time;
        $statement = $this->connection->prepare( "REPLACE INTO `session` 
                                                (`session_id`, `session_data`, 
                                                `expiry_time`)
                                                 VALUES (?, ?, ?)" );
        $statement->bind_param( "ssi", $session_id, $session_data, $expiry_time );

        if ( !$statement->execute() )
            return false;

        return true;
    }

    public function destroy( $session_id )
    {
        $statement = $this->connection->prepare( "DELETE FROM `session` 
                                                    WHERE `session_id` = ?" );
        $statement->bind_param( "s", $session_id );

        if ( !$statement->execute() )
            return false;

        return true;
    }

    public function gc( $max_lifetime )
    {
        $current_time = time();
        $statement = $this->connection->prepare( "DELETE FROM `session` 
                                                    WHERE `expiry_time` < ?" );
        $statement->bind_param( "i", $current_time );

        if ( !$statement->execute() )
            return false;

        return true;
    }

}

【问题讨论】:

  • 在destroy中记录一些东西,在gc中记录一些东西,这样你就知道哪个负责删除。或者使用调试器。
  • 如果您尝试不使用 session_write_close(); 会怎样?另外,你在什么服务器上运行这个?看看这个问题(见答案中的 cmets)stackoverflow.com/questions/10028643/…
  • @GeraldVersluis 重定向后数据仍在数据库中,因此它不是 session_write_close() 函数(尝试删除它但仍然没有运气)
  • @greg0ire 似乎两者都没有被调用,所以他们不是罪魁祸首。
  • @GeraldVersluis 我忘了补充,这是在使用 PHP 5.3.5 的 Apache 2.2.17 (Windows) 上运行的,尽管它最终会在 Linux 服务器上运行(虽然尚未在那里测试。)

标签: php mysql


【解决方案1】:

您清除的会话数据少于当前时间。它应该是当前时间 - max_lifetime

这里是解决办法

public function gc( $max_lifetime )
{
    $current_time = time() - $max_lifetime;
    $statement = $this->connection->prepare( "DELETE FROM `session` 
                                                    WHERE `expiry_time` < ?" );
    $statement->bind_param( "i", $current_time );

    if ( !$statement->execute() )
            return false;

    return true;
}

【讨论】:

  • 啊,这是我忽略的问题,但似乎不是问题。即使没有调用垃圾收集器,也会出现问题。感谢您纠正这一点,否则很可能会错过。
【解决方案2】:

在大量的文件日志记录和打印出变量之后(一些会话函数在发送到浏览器的输出后被调用,因此你不能以正常方式输出错误)原来问题出在我的数据库会话代码。

在将 MySQLi 输出绑定到变量后,我忘记添加“$statement->fetch()”,因此我从未真正从数据库中获取数据。

这是一个令人尴尬的错误,但它让我们明白了一个事实,即如果您的软件有问题,通常是您的代码有问题,而不是您使用的语言或库。

感谢那些评论和回答的人,因此在过去的几天里我学到了很多关于 PHP 会话的知识。

【讨论】:

    猜你喜欢
    • 2017-09-11
    • 2013-08-11
    • 2020-12-14
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 2014-11-05
    • 2011-03-02
    • 1970-01-01
    相关资源
    最近更新 更多