使用session_write_close()时PHP保存会话;

Jer*_*sia 20 php session

我有一个页面,我在这个页面的开头我要进行长时间的轮询

session_start();
session_write_close();
Run Code Online (Sandbox Code Playgroud)

因为:

为防止并发写入,任何时候只有一个脚本可以在会话上运行

因此,如果我不这样做并且长轮询正在运行,则用户将无法加载另一个页面.

因此,可以从此轮询页面访问我在会话中的数据,但在我的脚本中的某些时候,我要将会话保存回服务器,因为我对其进行了一些更改.

这样做的方法是什么?

这将是非常好的,这将是一种做某事的方式

session_write_open();
//do stuff
session_write_close();
Run Code Online (Sandbox Code Playgroud)

但是session_write_open()不存在!

谢谢

Jon*_*Jon 17

对会话进行一些更改之前,请session_start再次致电.进行更改,如果您仍然不想再次退出呼叫session_write_close.您可以根据需要多次执行此操作.


Arm*_*ier 10

以前的解决方案将创建会话ID和cookie ...我不会按原样使用它:

每次调用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);
Run Code Online (Sandbox Code Playgroud)

编辑:看下面CMCDragonkai的答案,看起来不错!?


rin*_*ogo 6

这里的其他答案提出了很好的解决方案.正如@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
Run Code Online (Sandbox Code Playgroud)

这可以防止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.
}
Run Code Online (Sandbox Code Playgroud)

原始相关的SO问题/答案:https://stackoverflow.com/a/12315542/114558


Kev*_*son 5

这里的所有答案似乎都在说使用会话方法,而这些方法显然不打算使用...即session_start()多次调用。

PHP网站提供了一个示例SessionHandlerInterface实现,该实现将与现有会话一样工作,但不锁定文件。仅仅实现它们的示例接口即可解决我的锁定问题,以允许在同一会话上进行并发连接,而​​不会限制我向会话添加var的能力。为了防止某些竞争情况,由于应用程序的会话不是完全无状态的,因此我确实必须设法在不关闭会话的情况下保存会话中的请求,以便重要的更改可以在更改后立即保存,而不太重要的会话var可以保存在请求结束时。请参阅以下示例以了解用法:

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';
Run Code Online (Sandbox Code Playgroud)

如果您更换Session::start()session_start()Session::save()session_write_close(),你会发现,后续请求将永远不会打印出的第三个变量...它将会丢失。但是,使用SessionHandler(如下),不会丢失任何数据。

OOP实现需要PHP 5.4+。但是,您可以在旧版本的PHP中提供单独的回调方法。参见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;
        }
    }
Run Code Online (Sandbox Code Playgroud)