7

Here's the thing. A colleague of mine is trying to overwrite the session handling of a framework that we use. This framework uses PHP's own native session handling by default, but he is now trying to implement a database layer between the requests.

Problem is that the database object is not available by the time sessions are written anymore, yet it is available for other functions such as when data is read from sessions. This is a wild behavior. Here's what we did:

register_shutdown_function('exithandler'); 

session_set_save_handler(
    'sess_open',
    'sess_close',
    'sess_read',
    'sess_write',
    'sess_destroy',
    'sess_gc'
);

Each of those functions also writes a single line to our log file that we can track with the name of the function. This is done whenever the function is called. Now here are two URL's that are requested, the first one is where sessions are actually written (new data to the sessions) and the second one where session data is just checked (and none is written). Here's the puzzle:

/login/
sess_open
sess_read
exithandler
sess_write
sess_close

/account/
sess_open
sess_read
sess_write
sess_close
exithandler

Why is this behavior different? Why is the exit handler called before data is stored in sessions and why is the same not true for a regular page, even though the same methods are indeed called?

Problem is that none of our classes are available anymore after the exithandler is called, I assume that PHP garbage collector has called __destruct() methods on all of our classes and they are gone. This is just bad.

Anyone knows why PHP behaves this way?

4

1 回答 1

2

正如您的评论所说的 PHP5.4,您可能想看看SessionHandlerInterface(). 您可以通过register_shutdown_functioninopen()方法来半自动化该过程并真正利用 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();
于 2012-09-06T15:33:48.503 回答