Bart Jacobs
Bart Jacobs

Reputation: 9082

PHP: Session stored in database not always updated

When storing sessions in a database, I have noticed that updating a session does not necessarily invoke the write function that was set in session_set_save_handler(). This problem seems to be solved when you call session_write_close() at the end of the script.

Am I missing something or is this indeed what needs to be done when storing sessions in a database using session_set_save_handler()?

The code that I am currently using.

// EXECUTE AT START OF SCRIPT
$session = new Session();
session_start();

Session Class

public function __construct() {
    $database       = Database::instance();
    $this->dbh      = $database->dbh();
    $this->lifetime = get_cfg_var('session.gc_maxlifetime');

    session_set_save_handler(
        array(&$this, 'open'),
        array(&$this, 'close'),
        array(&$this, 'read'),
        array(&$this, 'write'),
        array(&$this, 'destroy'),
        array(&$this, 'clean')
    );
}

// SESSION HANDLER METHODS
function open($savePath, $sessionName) {
    return true;
}

function close() {
    return true;
}

function read($sessionUuid) {
    logMessage('READ SESSION DATA > ' . $sessionUuid);
    $data = array();
    $time = time();

    $sth = $this->dbh->prepare("SELECT sessionData FROM phpSession WHERE sessionUuid = :sessionUuid AND expires > " . $time);
    $sth->setFetchMode(PDO::FETCH_ASSOC);

    try {
        $sth->execute(array('sessionUuid' => $sessionUuid));

        if ($sth->rowCount() == 1) {
            $row = $sth->fetch();
            $data = $row['sessionData'];
        }

    } catch (PDOException $exception) {
        // HANDLE EXCEPTION
    }

    return $data;
}

function write($sessionUuid, $sessionData) {
    logMessage('WRITE SESSION DATA > ' . $sessionUuid);
    $time = time() + $this->lifetime;

    $sth1 = $this->dbh->prepare("SELECT sessionUuid FROM phpSession WHERE sessionUuid = :sessionUuid");

    try {
        $sth1->execute(array('sessionUuid' => $sessionUuid));

        $query  = '';
        $data   = array(
            'sessionUuid'   => $sessionUuid,
            'sessionData'   => $sessionData,
            'expires'       => $time
        );

        if ($sth1->rowCount() == 1) {
            // UPDATE
            $query = "UPDATE phpSession SET sessionUuid = :sessionUuid, sessionData = :sessionData, expires = :expires";

        } else {
            // INSERT
            $query = "INSERT INTO phpSession (sessionUuid, sessionData, expires) VALUES (:sessionUuid, :sessionData, :expires)";
        }

        $sth2 = $this->dbh->prepare($query);

        try {
            $sth2->execute($data);

        } catch (PDOException $exception) {
            // HANDLE EXCEPTION
        }

    } catch (PDOException $exception) {
        // HANDLE EXCEPTION
    }

    return true;
}

function destroy($sessionUuid) {
    $sth = $this->dbh->prepare("DELETE FROM phpSession WHERE 'sessionUuid' = :sessionUuid");

    try {
        $sth->execute(array('sessionUuid' => $sessionUuid));

    } catch (PDOException $exception) {
        // HANDLE EXCEPTION
    }

    return true;
}

function clean() {
    $sth = $this->dbh->prepare("DELETE FROM phpSession WHERE 'expires' < UNIX_TIMESTAMP()");

    try {
        $sth->execute();

    } catch (PDOException $exception) {
        // HANDLE EXCEPTION
    }

    return true;
}

Upvotes: 0

Views: 263

Answers (2)

Terence Johnson
Terence Johnson

Reputation: 1647

For PHP < 5.4 you need to register session_write_close() as a shutdown function, so that the database write happens before the script execution cycle ends.

register_shutdown_function('session_write_close');

Upvotes: 2

mobius
mobius

Reputation: 5174

According to http://php.net/manual/en/function.session-write-close.php

Session data is usually stored after your script terminated without the need to call session_write_close(), but as session data is locked to prevent concurrent writes only one script may operate on a session at any time.

Locking is usually the reason for not having your session written. This might happen in framed pages (as the documentation states) or in lengthy ajax requests where more than one request happens simultaneously.

Upvotes: 3

Related Questions