【问题标题】:Reopening a session in PHP在 PHP 中重新打开会话
【发布时间】:2012-09-01 04:00:55
【问题描述】:

如何在 PHP 中重新打开会话而不收到标头已发送警告?

设置完所有我喜欢设置的会话变量后,我用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)上使用基于默认文件的处理程序时。为什么不直接使用更好的会话处理程序?
  • 不知道自定义会话处理程序会有所不同。尝试也是一件好事。谢谢!

标签: php apache session session-cookies


【解决方案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 是否是存储这些信息的最佳位置 - 是否可以将其记录到数据库中?你存储的数据到底是什么,为什么不能在产生输出之前生成?
猜你喜欢
  • 2012-05-17
  • 1970-01-01
  • 2016-08-12
  • 2021-04-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-14
  • 2014-03-05
  • 1970-01-01
相关资源
最近更新 更多