2012-04-06 73 views
16

我有一个网页,我做一个长轮询我在使用开始本页这个使用session_write_close()时PHP保存会话;

session_start(); 
session_write_close(); 

因为:

防止并发只写脚本可以进行操作在任何时候会话

所以,如果我不这样做,长轮询正在运行用户将无法加载另一个页面。

因此,从这个轮询页面访问我的会话数据是可能的,但在我的脚本中的某个时刻,我必须将会话保存回服务器,因为我对其进行了一些更改。

这样做的方法是什么?

这将是非常好的,它会是一个办法做到像

session_write_open(); 
//do stuff 
session_write_close(); 

但session_write_open()不存在!

感谢

+0

后知后觉,对未来的读者,我建议你使用'的session_set_save_handler()'为更多最佳实践,因为它不涉及任何变通办法,但修改会话作为PHP作者似乎有意。我已经发布了一个如何在下面执行此操作的示例。 – 2015-01-16 23:11:56

回答

13

你做出一些改变会话之前,再次调用session_start。进行更改,如果您仍然不想退出呼叫session_write_close。您可以根据需要随意多次执行此操作。

+0

为什么它没有'session_start'为我工作?我只是在我的脚本的开头创建session_write_close,包含很多逻辑,包括会话的更新,并且一切正常,会话更新正确 – 2017-01-19 12:27:07

+0

@VictorBredihin而不看实际的代码我不知道可能发生了什么。也许你可以发布一个新问题。 – Jon 2017-01-26 11:35:18

10

以前的解决方案将创建一个会话ID和饼干......因为是我不会用它:

会话创建每次调用session_start时间()。如果您想要 避免多个Cookie,请编写更好的代码。多个session_start() 特别是对于同一个脚本中的相同名称看起来像一个真正的 坏主意。

在这里看到:https://bugs.php.net/bug.php?id=38104

我要寻找一个解决方案,现在也是一样,我无法找到一个。我同意那些说这是“错误”的人。 您应该可以重新打开一个php会话,但正如您所说session_write_open()不存在...

我在上述线程中找到了解决方法。它涉及在处理请求之后发送一个标头,手动指定会话ID的cookie。幸运的是,我正在使用自制的前置控制器工作,以便任何分控制器都不会自行发送数据。简而言之,它适用于我的情况。要使用此功能,您可能只需使用ob_start()ob_get_clean()。这里是神奇的线:

if (SID) header('Set-Cookie: '.SID.'; path=/', true); 

编辑:看到CMCDragonkai的答案下面,似乎不错!?

+0

好吧,我不知道cookies,是调用session_start();没有什么意义,但我没有其他解决方案来解决我所知道的问题;)感谢有关错误的信息! – 2012-12-05 15:07:20

+0

这可能只是一个缺少的功能... – 2012-12-05 15:07:39

+1

找到了解决方法!编辑答案! – 2012-12-05 16:28:51

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()。

4

其他答案在这里提出了很好的解决方案。正如@Jon所提到的,诀窍是在想要进行更改之前再次调用session_start()。然后,当您完成更改时,请再次调用session_write_close()。

正如@Armel Larcier所提到的,问题在于PHP尝试生成新的头文件并且可能会生成警告(例如,如果您已经向客户端写入了非头文件数据)。当然,你可以简单地用“@”(@session_start())作为session_start()的前缀,但有一个更好的方法。

另一个堆栈溢出的问题,通过提供@VolkerK揭示了最好的回答:

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. 
} 

原来那么相关的提问/回答:https://stackoverflow.com/a/12315542/114558

+1

这是一个很好的发现。 *最好*解决方案将是从不输出内容块(总是为我工作),在这种情况下,你会得到多头,但他们是无害的。然而这可能不可能,例如,你已经继承了一些代码,所以这个解决方法有一个有效的用例。 – Jon 2013-07-30 09:02:18

+0

当你只能使用session_set_save_handler并且完全避免这个问题并且让它做你想做的事时,这看起来很复杂。 – 2015-01-16 23:08:32

3

这里所有的答案似乎在说以明显不打算使用会话方法的方式使用会话方法......即多次呼叫session_start()

PHP网站提供了一个示例SessionHandlerInterface实现,它将像现有会话一样工作,但不锁定文件。只是实现他们的示例接口修复了我的锁定问题,以允许在同一个会话上进行并发连接,而​​不会限制将会话添加到会话的能力。为了防止一些竞争条件,因为应用程序的会话不是完全无状态的,所以我必须设法在不中断请求的情况下保存会话,以便重要更改可以在更改后立即保存,不太重要的会话更改可以保存在请求结束时。见下面的例子为使用:

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; 
     } 
    }