0

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;
}
3
  • 1
    just a small notice on your class and not the problem (for which you have an answer): in the last two methods (destroy and clean) I see that you enclose your column names in the queries between apostrophes and not backticks. does that work for you? Commented Nov 17, 2011 at 8:23
  • Thanks for your comment. I haven't run into any problems by using apostrophes so far. Commented May 21, 2012 at 3:41
  • I don't remember this comment but I still find it strange that you are using apostrophes for your column names. Take these two samples: codepad.org/n7mOQz5h and codepad.org/NeY7a7nn The first one is a from your destroy method and the second replaces the apostrophes with backticks. The first one (yours) will compare the string 'sessionUuid' to the session id and it will always be false. The second one will compare the value from that column to the parameter and sometimes will match rows. You can run a test and you will see that no records are deleted using your query Commented May 21, 2012 at 4:46

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.