Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Here's a true challenge: why does PHP call shutdown function before sessions are written?

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?

like image 359
kingmaple Avatar asked Sep 04 '12 17:09

kingmaple


1 Answers

As your comments say PHP5.4, you may want to take a look at the SessionHandlerInterface(). You can pass the register_shutdown_function in the open() method to semi-automate the process and really take advantage of the PHP5.4 features.

<?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();
like image 184
Mike Mackintosh Avatar answered Nov 15 '22 01:11

Mike Mackintosh