PHP:在多个选项卡中称为“同时”的session_start()会创建多个会话

时间:2013-06-27 17:52:51

标签: php session

我偶然发现了一个有趣的问题。我正在研究的网站目前有三个页面:login.php和welcome.php以及ajax.php。它们都在代码顶部调用session_start()。 Login.php检查$ _SESSION []变量以查看是否有人登录;如果没有,它获取他们的名字/密码,联系ajax.php检查名称/密码,并通过设置适当的$ _SESSION []变量将其登录。 Welcome.php查找相应的$ _SESSION []变量并向用户显示欢迎消息 - 如果未设置,则要求用户请登录。典型的行为,如果以典型的方式使用它可以很好地工作。

但是,如果您将login.php和welcome.php标记为一组标签(例如,在Firefox中),然后在同时打开它们,则会发生奇怪的事情。也许是因为session_start()在同一时间被调用了两次(通过使用error_log()进行检查),所以创建了两个单独的会话(使用session_id()进行检查)。无论哪个session_start()恰好被称为“last”(尽管相同的时间戳),仍然是会话。这会导致以下情况出现问题:在调用 welcome.php的session_start()之前,login.php的session_start()被称为

在这种情况下,只要没有刷新,在login.php中创建的会话将继续存在于该页面中。但是,当它联系ajax.php来检查name / pwd细节,并且ajax.php调用session_start()时,它会检索welcome.php创建的会话,该会话在$ _SESSION []变量中没有任何内容,从而完成了整个事情。失败。因此,如果login.php首先调用session_start(),我必须找到一种方法来阻止welcome.php的session_start()创建一个新的。注意:调用顺序不一致,只要login.php的session_start()最后调用(我无法控制),一切都显然正常。

理想情况下,第二个文件会有一些方法可以在创建会话的过程中注意到第一个文件是,所以它不应该创建它自己的(但仅限于欢迎。 PHP)。

这必须是其他人处理的问题,但我完全无法在这些互联网上找到任何提及它的问题。任何帮助将不胜感激。

EDIT1:以下是重现问题所需的文件:

的login.php

<?php
session_start();
error_log("login.php, session id: ".session_id());
$_SESSION['user'] = "EleventyOne"
?>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>multiple session bug - login</title>

<script src='jquery-1.9.1.js'></script>
<script>

    $(document).ready(function() {
        $.ajax({
                url : 'ajax.php',
                data : { 'func' : 'check_login' },
                dataType : 'json',
                type : 'GET',
                timeout : 10000
            })
        .done(function(data,textStatus,jqXHR){
            alert("Done: "+data.status);
        })
        .fail(function(jqXHR,textStatus,errorThrown) {                    
            alert("Failed: " + textStatus + "(" + errorThrown + ")");
        });
    }); // end ready

</script>
</head>

<body>
</body>
</html>

的welcome.php:

<?php
session_start();
error_log("welcome.php, session id: ".session_id());
$message = "";

if ( isset($_SESSION['user']) ) {
    $message = "Hello, ".$_SESSION['user'];
}
else {
    $message = "Please login!";
}
?>

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>multiple session bug - welcome</title>
</head>

<body>
<div>
<?php echo $message; ?>
</div>
</body>
</html>

AJAX.PHP:

<?php
session_start();
error_log("ajax.php, session id: ".session_id());

// ignoring $_GET[] here, as superfluous to the point...

if ( isset($_SESSION['user']) ) {

    // check the database for that user...
    // ...

    // return status
    $ret['status'] = "ok";
    echo json_encode($ret);
    exit;
}
else {
    // return failed status
    $ret['status'] = "broken session";
    echo json_encode($ret);
    exit;
}
?>

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>multiple session bug - ajax</title>

<script src='_js/jquery-1.9.1.js'></script>
<script>

    $(document).ready(function() {
    }); // end ready

</script>
</head>

<body>
</body>
</html>

如果你加载login.php,打开一个新选项卡并加载welcome.php,这是你得到的error_log文件(一切都很好):

[27-Jun-2013 18:39:40 UTC] login.php, session id: skofpr8g0tg81aqohnkahv3vk5
[27-Jun-2013 18:39:40 UTC] ajax.php, session id: skofpr8g0tg81aqohnkahv3vk5
[27-Jun-2013 18:39:44 UTC] welcome.php, session id: skofpr8g0tg81aqohnkahv3vk5

如果您将login.php和welcome.php标记为一组标签,请关闭浏览器,重新打开它,同时打开两个标签,您将获得两个error_log文件中的一个,具体取决于哪个session_start被称为第一。

这个工作正常,因为login.php的session_start()仍然存在于ajax.php。所以ajax.php报告“ok”的状态:

[27-Jun-2013 18:40:39 UTC] welcome.php, session id: 6q2q96lhhoaqqhj214gs3gos36
[27-Jun-2013 18:40:39 UTC] login.php, session id: j8eaa5mtfsla9q3q80qt03kvt7
[27-Jun-2013 18:40:39 UTC] ajax.php, session id: j8eaa5mtfsla9q3q80qt03kvt7

这个不起作用,因为welcome.php的session_start()持续存在于ajax.php,所以ajax.php会报告“已断开的会话”:

[27-Jun-2013 18:40:18 UTC] login.php, session id: s4b7jo41jpg1ubbe8at7c5qr35
[27-Jun-2013 18:40:18 UTC] welcome.php, session id: freu86sn3edc3fuoc2pn875o90
[27-Jun-2013 18:40:18 UTC] ajax.php, session id: freu86sn3edc3fuoc2pn875o90

2 个答案:

答案 0 :(得分:1)

我能提出的最佳解决方案是将会话“创建”(即设置会话变量)移动到ajax.php文件中,只有在用户成功发送了uname / pwd之后才会执行,因此它们是关于无论如何都要重定向到新页面(即welcome.php)。这意味着无法保证login.php能够访问由ajax.php设置的任何会话变量,所以它只是一个仅仅依赖于其ajax调用才能知道发生了什么的愚蠢页面。事实证明,这毕竟不是那么麻烦。

答案 1 :(得分:0)

如果覆盖另一个,我怀疑一个过程。任何时候只有一个进程可以打开相同的会话文件。第二个(及后续)将等待会话文件变为可用。

根据我的经验,$ _SESSION并不像看起来那么神奇,它只是使用浏览器cookie或其他东西来确保每次都在磁盘上读取和写入相同的文件。我以前只是在需要时使用session_start()解决了这个问题。换句话说,我不是在每个页面的顶部打开会话,而是在写入或从$ _SESSION中检索信息之前立即执行了session_start,并在完成后立即执行了session_write_close。