这是一个真正的挑战:为什么PHP会在编写会话之前调用shutdown函数?

kin*_*ple 7 php debugging session garbage-collection shutdown

这就是事情.我的一位同事试图覆盖我们使用的框架的会话处理.默认情况下,此框架使用PHP自己的本机会话处理,但他现在正尝试在请求之间实现数据库层.

问题是数据库对象在编写会话时不可用,但它可用于其他功能,例如从会话中读取数据时.这是一种疯狂的行为.这是我们做的:

register_shutdown_function('exithandler'); 

session_set_save_handler(
    'sess_open',
    'sess_close',
    'sess_read',
    'sess_write',
    'sess_destroy',
    'sess_gc'
);
Run Code Online (Sandbox Code Playgroud)

这些函数中的每一个也会在我们的日志文件中写入一行,我们可以使用该函数的名称进行跟踪.无论何时调用该函数,都会执行此操作.现在这里有两个请求的URL,第一个是实际写入会话的地方(会话的新数据),第二个是刚刚检查会话数据的(并且没有写入).这是谜题:

/login/
sess_open
sess_read
exithandler
sess_write
sess_close

/account/
sess_open
sess_read
sess_write
sess_close
exithandler
Run Code Online (Sandbox Code Playgroud)

为什么这种行为有所不同?为什么在将数据存储在会话中之前调用退出处理程序,为什么对于常规页面而言同样如此,即使确实调用了相同的方法?

问题是在调用exithandler之后我们的类都不再可用了,我假设PHP垃圾收集器在我们所有的类上都调用了__destruct()方法,它们就不见了.这很糟糕.

任何人都知道为什么PHP会这样做?

Mik*_*osh 2

正如您的评论所说的 PHP5.4,您可能想看看SessionHandlerInterface(). register_shutdown_function您可以在方法中传递open()来半自动化该过程并真正利用 PHP5.4 功能。

<?php
class MySessionHandler implements SessionHandlerInterface
{
    private $savePath;

    public function open($savePath, $sessionName)
    {
        register_shutdown_function('session_write_close');
        $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;
    }
}

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();
Run Code Online (Sandbox Code Playgroud)