3

A question with respect to Session Expiration in PHP.

I need my server to throw away session information if that user has been inactive for a while (for testing purposes, 5 seconds).

I've looked at this question and particular at the answer by Gumbo (+28 votes) and I've been wondering about the feasibility of this answer with respect to inactive users. On my site I already implemented this suggestion and it works fine, so long as the user requests some data at least once after the session expired. But the problem with inactive users is that they don't request new data. So the expiration code is never called.

I've been looking at session.gc_maxlife and associated parameters in my PHP.ini, but I couldn't make this work the way I wanted it to.

Any suggestions on this problem?

Community
  • 1
  • 1
Timo
  • 2,212
  • 2
  • 25
  • 46
  • If you don’t want an expired session to be destroyed on the next request, what do you expect to happen instead? – Gumbo Sep 03 '10 at 14:34
  • 1
    I see no problem here, unless you depend on the expiration code to do some sort of extra cleanup. If that's the case then maybe a cron script would be a better choice? – Manos Dilaverakis Sep 03 '10 at 14:37
  • I do want it destroyed, but don't want to depend on the user making another request before the session actually gets destroyed. – Timo Sep 03 '10 at 14:39
  • you have misunderstood: the garbage collection works on _any_ user triggering it for all users expired. No need for a specific user to make an expired call before his data is purged. – Wrikken Sep 03 '10 at 14:48
  • I want to thank everybody, especially Gumbo and Saul for their responses. Since actually do need to administer some database changes on a session expiration, I picked Saul's answer. Gumbo's answer helped out as well, but it made me realize that I would not be able to see that the session actually expired without running an additional job that can also modify the database to reflect the expired sessions. – Timo Sep 06 '10 at 07:15

4 Answers4

3

The session expiration logic I mentioned does already do what you’re expecting: The session cannot be used once it has expired.

That the session data is still in the storage doesn’t matter as it cannot be used after expiry; it will be removed when the garbage collector is running the next time. And that happens with a probability of session.gc_probability divided by session.gc_divisor on every session_start call (see also How long will my session last?).


Edit    Since you want to perform some additional tasks on an expired session, I would rather recommend to use a custom session save handler.

When using a class for the session save handler, you could write two classes, one for the basics save handler and one with a extended garbage collector that performs the additional tasks, e.g.:

interface SessionSaveHandler {
    public function open();
    public function close();
    public function read($id)
    public function write($id, $data);
    public function destroy($id);
    public function gc($callback=null);
}
class SessionSaveHandler_WithAdditionalTasks implements SessionSaveHandler {
    // …
    public function gc($callback=null) {
        if (!is_null($callback) && (!is_array($callback) || !is_callable($callback))) return false;
        while (/* … */) {
            if ($callback) $callback[0]::$callback[1]($id);
            // destroy expired sessions
            // …
        }
    }
    public static function doAdditionalTasksOn($id) {
        // additional tasks with $id
    }
}
session_set_save_handler(array('SessionSaveHandler_DB_WithAdditionalTasks', 'open'),
                         array('SessionSaveHandler_DB_WithAdditionalTasks', 'close'),
                         array('SessionSaveHandler_DB_WithAdditionalTasks', 'read'),
                         array('SessionSaveHandler_DB_WithAdditionalTasks', 'write'),
                         array('SessionSaveHandler_DB_WithAdditionalTasks', 'destroy'),
                         array('SessionSaveHandler_DB_WithAdditionalTasks', 'gc')
                         );
Community
  • 1
  • 1
Gumbo
  • 643,351
  • 109
  • 780
  • 844
1

The garbage collector isn't called per-session, the garbage collector has a change to be called based on tge gc_* values, which can invalidate multiple sessions. So as long as someone triggers it, other people can be logged out. If you need a more reliable method, if your timeout is in minutes use a cronjob, if your timeout is in seconds you'll have to use some kind of daemon process.

Wrikken
  • 69,272
  • 8
  • 97
  • 136
1

If you need to call specific expiration logic (for example, in order to update a database) and want independence from requests then it would make sense to implement an external session handler daemon that looks at access times of session files. The daemon script should execute whatever necessary for every session file that has not been accessed for a specified time.

This solution has two prerequisites: the server's filesystem supports access times (Windows does not) and you can read files from session save path.

Saul
  • 17,973
  • 8
  • 64
  • 88
  • Just to be a pedant, Windows [does use accessed times](http://msdn.microsoft.com/en-gb/library/windows/desktop/ms724290(v=vs.85).aspx) - and has done since the start - although it's been [disabled by default](http://superuser.com/a/251265/11305) on recent versions for performance reasons. FAT systems only allowed a resolution of 1 day! But NTFS is far more precise. It's still not an ideal fit as NTFS can delay storing the new timestamp until the write won't impact disk performance (worst-case, up to an hour). – Basic May 31 '14 at 22:55
1

One way you can do this is using javascript to refresh the page a little after the timeout period. Granted your users will have to have Javascript enabled for this to work. You can also add extra features like having javascript pop up a timeout notice with a count down, etc. So essential what happens the session is expired due to your settings, then the refresh hits, clean up runs and your done.

<html>
<head>
<script type="text/JavaScript">
<!--
function timedRefresh(timeoutPeriod) {
    setTimeout("location.reload(true);",timeoutPeriod);
}
//   -->
</script>
</head>
<body onload="JavaScript:timedRefresh(5000);">
</body>
</html>
Grayslin
  • 11
  • 2
  • Yup, using a heartbeat is an option but in your example the "onUnload" event needs a good-bye function. And even then the expiry event would not be guaranteed if the useragent simply drops the connection. But judging from the accepted answer, Timo was not looking for a bulletproof way to execute custom code on the server when a session expires. I gather he was simply worried about session destruction. – Saul Sep 05 '10 at 14:42