在 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在登录/退出时重新生成会话ID不起作用

每当我重新加载页面时,仍会设置取消设置 PHP 会话变量 [重复]

使用 HTML <form action method=GET> 在 PHP 页面中重新启动会话

Asp.Net:关闭并重新打开浏览器实例后保留旧的浏览器会话