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')
);