在 PHP 中重新打开会话
Posted
技术标签:
【中文标题】在 PHP 中重新打开会话【英文标题】:Reopening a session in PHP 【发布时间】:2012-09-01 04:00:55 【问题描述】:如何而不收到标头已发送警告?
设置完所有我喜欢设置的会话变量后,我用session_write_close() 关闭会话。我这样做是因为只要会话打开,同一个客户端可能只有一个活动连接。但我喜欢有多个并行的。
但是,如果我想稍后设置另一个会话变量,我需要再次使用 session_start() 重新打开会话。这可行,但由于我已经将代码发送到客户端,它会打印“标头已发送”警告。为什么它试图再次设置cookie? cookie 已经设置好了。我唯一需要做的就是重新获得在服务器上写入会话文件的权限。
好吧,我可以压制他们。但是有没有办法重新打开一个会话,它已经用 session_write_close 关闭而不重新发送 Cookie-header?第一个 session_start() 已经正确发送了 Cookie-header。所以第二个只需要让我重新访问存储在 Web 服务器上的会话文件。
<?php
session_start();
// setting all the session vars I like to set
session_write_close(); // <-- // To allow parallel requests by the same user, while this script is still running
// Code that takes some time to execute
// It also prints output, so no more cookie headers after this point
@session_start(); // <-- works, but I like to use it without suppressing warnings
$_SESSION['key'] = 'new value I like to store';
session_write_close();
?>
【问题讨论】:
您可以禁止警告,但会话不会启动,因为标头已经发送。您需要在向浏览器输出任何内容之前启动会话,别无他法。 不,会话将重新开始。有用。这是因为第一个 session_start() 已经正确发送了 cookie 更好地构建您的应用程序流程,以便您首先在会话中完成所有需要做的事情。将内容输出到浏览器应该是您做的最后一件事;在您组装 html 时,您的所有业务逻辑都应该已经完成,并且在输出已经开始后不需要写入会话。I do this, because as long as the session is open...
- 仅当您在具有大量文件锁定的机器(即 MSWindows)上使用基于默认文件的处理程序时。为什么不直接使用更好的会话处理程序?
不知道自定义会话处理程序会有所不同。尝试也是一件好事。谢谢!
【参考方案1】:
session_start();
...
session_write_close();
...
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
ini_set('session.use_trans_sid', false);
ini_set('session.cache_limiter', null);
session_start(); // second session_start
这将阻止 php 第二次调用php_session_send_cookie()
。See it working。
虽然重组脚本似乎仍然是更好的选择...
对于 PHP 7.2+,您基本上需要重新实现会话 cookie 以避免错误和警告:
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
ini_set('session.use_trans_sid', false);
ini_set('session.cache_limiter', null);
if(array_key_exists('PHPSESSID', $_COOKIE))
session_id($_COOKIE['PHPSESSID']);
else
session_start();
setcookie('PHPSESSID', session_id());
session_write_close();
session_start();
...
session_write_close();
...
session_start(); // second session_start
【讨论】:
有趣的想法。暂定+1,我测试一下。ini_set('session.cache_limiter', null);
已添加 - 尚未测试,但这也是与会话相关的 http 标头。
是的,第四个选项修复了它。这是一个可行的解决方案。 codepad.viper-7.com/4gz38n(查看历史上的所有三项)
我发现有必要保存会话ID,比如$session_id = session_id()
(在session_write_close()
之前),然后像session_start($session_id)
一样使用它。否则我重新打开的会话是空的。此外,我正在使用这种技术定期重新打开会话以检查用户是否仍然登录,在脚本中实现服务器发送的事件。
这在 php 7.2 中不再起作用 :( "session_start(): 标头已发送时无法启动会话"【参考方案2】:
EDIT看@VolkerK的解决方案,比这个好。
只需在脚本执行时缓冲脚本的输出,以防止发送标头,并在最后输出:
<?php
session_start();
// setting all the session vars I like to set
session_write_close();
// Start output buffer
ob_start();
// Code that takes some time to execute
// Do session stuff at the end of the script
session_start();
$_SESSION['key'] = 'new value I like to store';
session_write_close();
// Send output to client
ob_end_flush();
参考:Output control functions
【讨论】:
那行得通。但我想我宁愿只是抑制警告,而不是使用输出缓冲,这会减慢内容到达客户端的速度。 @JochenJung 实际上,在这种情况下,您将不得不取消警告。你不能同时拥有它——因为session_start()
试图设置一个标题,你不能在发送标题后调用它而不发出警告。现在我在很大程度上同意你的观点,这是不可能的,真正需要的是session_lock()
/session_unlock()
,它将释放会话文件,以便并发请求可以使用它,而不必发送再次标题。不幸的是,这就是你所坚持的。
@JochenJung 我想另一个选择是set_error_handler()
来“处理”不可避免的错误,但归根结底,当这样使用时,这只是一个更复杂的@
运算符。我理解您希望不惜一切代价避免@
(这是值得称赞的),但在这种特定情况下,我认为这是可以接受的。只需确保在代码中正确注释即可。
@JochenJung 还要考虑$_SESSION
是否是存储这些信息的最佳位置 - 是否可以将其记录到数据库中?你存储的数据到底是什么,为什么不能在产生输出之前生成?以上是关于在 PHP 中重新打开会话的主要内容,如果未能解决你的问题,请参考以下文章
PHP会话的数据在页面重新加载之间不一致 - 但会话ID是相同的
为啥在ios中重新打开会话后无法访问facebook好友列表
每当我重新加载页面时,仍会设置取消设置 PHP 会话变量 [重复]