我有一个页面,我会在本页开头使用这个
进行长时间的轮询session_start();
session_write_close();
因为:
为了防止并发写入,任何时候只有一个脚本可以在会话上运行
因此,如果我没有并且长轮询正在运行,则用户将无法加载另一页。
因此,可以从此轮询页面访问会话中的数据,但在我的脚本中的某些时候我会将会话保存回服务器,因为我对其进行了一些更改。
这样做的方法是什么?
这将非常好,它将成为一种像
这样的方式session_write_open();
//do stuff
session_write_close();
但是session_write_open()不存在!
由于
答案 0 :(得分:16)
在之前,您对会话进行了一些更改,再次致电session_start
。进行更改,如果您仍然不想再次退出呼叫session_write_close
。您可以根据需要多次执行此操作。
答案 1 :(得分:10)
之前的解决方案将创建会话ID和Cookie ...我不会按原样使用它:
每次调用session_start()时都会创建会话。如果你想 避免多个cookie,编写更好的代码。多个session_start() 特别是对于相同的脚本中的相同名称似乎是真的 坏主意。
见这里:https://bugs.php.net/bug.php?id=38104
我现在正在寻找解决方案而且我找不到解决方案。我同意那些说这是一个" bug"的人。
您应该可以重新打开php会话,但正如您所说session_write_open()
不存在...
我在上面的帖子中找到了一个解决方法。它涉及在处理请求后发送一个标题,指定会话ID的cookie。幸运的是,我正在使用自制的前置控制器,这样可以让任何子控制器都不会自己发送数据。
简而言之,它完全适用于我的情况。要使用此功能,您可能只需使用ob_start()
和ob_get_clean()
。这是神奇的界限:
if (SID) header('Set-Cookie: '.SID.'; path=/', true);
编辑:请参阅下面的CMCDragonkai的答案,看起来不错!?
答案 2 :(得分:5)
这里的其他答案提供了非常好的解决方案。正如@Jon所提到的,诀窍是在你想要进行更改之前再次调用session_start()。然后,当您完成更改后,再次调用session_write_close()。
正如@Armel Larcier所提到的,问题在于PHP尝试生成新标头并且可能会生成警告(例如,如果您已经将非标头数据写入客户端)。当然,你可以简单地在session_start()前加上“@”(@session_start()),但是有更好的方法。
由@VolkerK提供的另一个Stack Overflow问题揭示了最佳答案:
session_start(); // first 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); //May be necessary in some situations
ini_set('session.cache_limiter', null);
session_start(); // second session_start
这可以防止PHP再次尝试发送标头。你甚至可以编写一个辅助函数来包装ini_set()函数,以使它更方便:
function session_reopen() {
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
//ini_set('session.use_trans_sid', false); //May be necessary in some situations
ini_set('session.cache_limiter', null);
session_start(); //Reopen the (previously closed) session for writing.
}
原始相关的SO问题/答案:https://stackoverflow.com/a/12315542/114558
答案 3 :(得分:4)
这里的所有答案似乎都是说使用会话方法明显不打算使用它们......即不止一次调用session_start()
。
PHP网站提供了一个示例SessionHandlerInterface实现,它将像现有会话一样工作但不锁定文件。只是实现他们的示例接口修复了我的锁定问题,以允许同一会话上的并发连接,而不限制我将vars添加到会话的能力。为了防止某些竞争条件,由于应用程序的会话不是完全无状态的,我确实必须在不关闭它的情况下保存会话中间请求,以便在更改后立即保存重要更改,并且可以节省不太重要的会话变量在请求结束时。请参阅以下示例以了解用法:
Session::start();
echo("<pre>Vars Stored in Session Were:\n");print_r($_SESSION);echo("</pre>");
$_SESSION['one'] = 'one';
$_SESSION['two'] = 'two';
//save won't close session and subsequent request will show 'three'
Session::save();
$_SESSION['three'] = 'three';
如果您将Session::start()
替换为session_start()
而Session::save()
替换为session_write_close()
,您会注意到后续请求永远不会打印出第三个变量...它会迷路了。但是,使用SessionHandler(下面),不会丢失任何数据。
OOP实现需要PHP 5.4+。但是,您可以在旧版本的PHP中提供单独的回调方法。 See docs
namespace {
class Session implements SessionHandlerInterface {
/** @var Session */
private static $_instance;
private $savePath;
public static function start() {
if( empty(self::$_instance) ) {
self::$_instance = new self();
session_set_save_handler(self::$_instance,true);
session_start();
}
}
public static function save() {
if( empty(self::$_instance) ) {
throw new \Exception("You cannot save a session before starting the session");
}
self::$_instance->write(session_id(),session_encode());
}
public function open($savePath, $sessionName) {
$this->savePath = $savePath;
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
public function close() {
return true;
}
public function read($id) {
return (string)@file_get_contents("$this->savePath/sess_$id");
}
public function write($id, $data) {
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
public function destroy($id) {
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
public function gc($maxlifetime) {
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
}
答案 4 :(得分:3)
在测试了Armel Larcier的工作之后。这是我提出的解决这个问题的方法:
ob_start();
session_start();
session_write_close();
session_start();
session_write_close();
session_start();
session_write_close();
session_start();
session_write_close();
if(SID){
$headers = array_unique(headers_list());
$cookie_strings = array();
foreach($headers as $header){
if(preg_match('/^Set-Cookie: (.+)/', $header, $matches)){
$cookie_strings[] = $matches[1];
}
}
header_remove('Set-Cookie');
foreach($cookie_strings as $cookie){
header('Set-Cookie: ' . $cookie, false);
}
}
ob_flush();
这将保留在使用会话之前创建的任何Cookie。
顺便说一句,您可能希望将上述代码注册为register_shutdown_function的函数。确保在函数前运行ob_start(),在函数内运行ob_flush()。