我的一些PHP代码遇到了一个非常奇怪的问题。我打开了错误报告,但也没有出现任何问题。
基本上我有一个将数据推送到 $_SESSION[] 的表单。代码为:
<?php
session_start();
$_SESSION['contact']['name'] = $_GET['name'];
$_SESSION['contact']['email'] = $_GET['email'];
$_SESSION['contact']['question'] = $_GET['question'];
session_write_close();
header('Location: confirm.php');
exit;
?>
这是有效的。但是,完全随机确认.php将或不接收此数据。如果我多次刷新,我有时会得到数据,有时不会。
这是确认代码:
<?php
ini_set('display_errors', 'on');
error_reporting(-1);
session_start();
print "SeshID:" . session_id() . "<br>";
print "CookieID:" . $_COOKIE['PHPSESSID'] . "<br>";
print "Status" . session_status() . "<br>";
print "Loc" . session_save_path() . "<br>";
print "Cookie";
print_r(session_get_cookie_params());
print "<br>Data:<br>";
print_r($_SESSION);
?>
同样,没有错误。没有出现任何看起来明显不对劲的东西。
奇怪的是,有时当我刷新确认页面时,我会得到一个空的"数组( )"。有时我会得到"数组([联系人]=>数组([问题]=>测试[名称]=>[电子邮件]=> test@test.com))"似乎没有理由...
我在执行此操作之间没有运行其他脚本 - 只有上面检查会话内容(确认)的脚本需要重新运行,并且只有数据以某种方式更改。我运行了几次表单,然后这个检查脚本,它完全不一致。会话和 Cookie ID 永远不会更改,并且始终相同
我缺少一些 PHP 设置吗?对我来说真的很奇怪,这些似乎正在提取已被覆盖的旧数据。
解决了!
我会为有同样问题的人发布我自己的答案。感谢在评论中帮助我发现这一点的人!
问题所在
问题是服务器正在使用群集或分发网络(挖掘Azure)来提供PHP文件。
第一个调用将会话数据写入计算机 1,然后从计算机 2 读取确认,该计算机没有正确的信息。在集群中,/tmp 通常不镜像,这是存储会话数据的位置。请与群集管理员讨论此问题。- 巴特·弗里德里希斯
似乎表单正在发送并将会话变量存储在服务器 1 上,但拉取会话数据的页面是服务器 2、服务器 3,有时是服务器 1 - 因此看到先前提交和偶尔正确数据的奇怪行为。
修复
这里有两个可能的修复方法。一:
会话存储目录未在所有服务器上镜像
在这种情况下,请通过更改 php 中的"session.save_path"来更改会话目录.ini或使用
session_save_path(dirname(__FILE__). '/sessions/');
在调用 session_start() 之前,在受影响文件的顶部(这是我们网站中唯一使用会话的部分,因此这是一个很好的/肮脏的修复)。
这里的重点是将其更改为一个目录,该目录肯定会在群集中的每个服务器上镜像。
会话正在以未镜像的另一种方式保存
在我的实际案例中,我发现我的会话处理程序是问题所在。通过使用
phpinfo();
我发现我的session.save_handler是"wincache",这是一个加速PHP的插件。它似乎这样做的方法之一是使用共享内存或其他东西来加速会话数据的加载。
无论哪种方式,这都不适合我的设置,我只需将变量更改为使用默认值:"files"即可修复它。
同样,最好的方法是使用 php.ini 并将session.save_handler设置为"files",但我无法访问该文件,因此,同样,我只是在 session_start() 之前的两个文件的标头中使用了调用。
ini_set("session.save_handler", "files");
我不推荐这样做,因为你知道当新的开发人员进来并且你忘记时,它会出现并在后面咬你 - 最好改变你的.ini
无论如何,就是这样。
感谢大家的帮助!
旁白
许多服务器解决方案(如 Azure)实际上都提供了自己的插件和会话处理程序,专门用于应对这种情况。如果可能的话,你当然应该使用它们!
刷新页面后,会话仍会进行身份验证,但我们丢失了使用指令手动添加的内容试试这个(this.set('session.X', 'Y'));