Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/237.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Session_write_close()时PHP保存会话;_Php_Session - Fatal编程技术网

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

使用Session_write_close()时PHP保存会话;,php,session,Php,Session,我有一个页面,在这里我做了一个很长的轮询,我必须在这一页的开头使用 session_start(); session_write_close(); 因为: 为了防止并发写入,在任何时候,一个会话上只能运行一个脚本 因此,如果我不这样做,并且长轮询正在运行,用户将无法加载另一个页面 因此,从这个轮询页面访问会话中的数据是可能的,但在脚本中的某个时刻,我必须将会话保存回服务器,因为我对它做了一些更改 怎么做 那将是非常好的,这将是一种做类似事情的方式 session_write_open(); /

我有一个页面,在这里我做了一个很长的轮询,我必须在这一页的开头使用

session_start();
session_write_close();
因为:

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

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

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

怎么做

那将是非常好的,这将是一种做类似事情的方式

session_write_open();
//do stuff
session_write_close();
但是session\u write\u open()不存在


谢谢

在更改会话之前,请再次呼叫
会话\u start
。进行更改,如果您仍然不想退出,请再次呼叫
会话\u write\u close
。您可以任意多次执行此操作。

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

每次调用Session_start()时都会创建会话。如果你愿意 要避免多个cookie,请编写更好的代码。多会话启动() 特别是对于同一剧本中的同一个名字来说,这似乎是一个非常重要的问题 坏主意

请看这里:

我现在也在寻找解决方案,但我找不到。我同意有人说这是一个“错误”。 您应该能够重新打开php会话,但正如您所说的,
session\u write\u open()
不存在

我在上面的线程中找到了一个解决方法。它包括在处理请求后发送一个标头,手动指定会话id的cookie。幸运的是,我正在使用一个自制的前端控制器,这样子控制器就不会单独发送数据了。 简言之,它在我的情况下非常有效。要使用它,您可能只需要使用
ob\u start()
ob\u get\u clean()
。这是一条神奇的线:

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

编辑:见下面CMCDRANGKAI的答案,看起来不错

测试完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\u shutdown\u函数的函数。确保在函数之前运行ob_start(),在函数内部运行ob_flush()。

这里的其他答案提供了很好的解决方案。正如@Jon所提到的,诀窍是在您想要进行更改之前再次调用session_start()。然后,在完成更改后,再次调用session\u write\u 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再次尝试发送标题。您甚至可以编写一个helper函数来包装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问题/答案:

这里的所有答案似乎都在说,使用会话方法的方式显然不是有意使用的……即多次调用
session\u 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';

好的,我不知道cookies的事,是的,调用session_start();没有多大意义,但我没有其他解决方案来解决我意识到的问题;)谢谢你提供关于这个错误的信息!这是个好发现。最好的解决方案是永远不要以块的形式输出内容(对我来说总是有效的),在这种情况下,你会得到多个标题,但它们是无害的。但是,如果您继承了一些代码,那么这可能是不可能的,因此此解决方案有一个有效的用例。当您可以使用会话集保存处理程序并完全避免问题并让它执行您想要的操作时,这似乎非常复杂。对于以后的读者,我建议您使用
会话集保存处理程序()
更像是一种最佳实践,因为它不涉及任何变通方法,而是按照PHP作者的意图修改会话。我在下面贴了一个例子来说明如何做到这一点。为什么没有“session_start”它对我有效?我只是在脚本的一开始就关闭了session_write_,包含了很多逻辑,包括session的更新,一切都很好,session更新了correctly@VictorBredihin如果不看实际的代码,我不知道会发生什么。也许你可以发布一个新问题。
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;
        }
    }