Strange Sessions Problem

I’ve got a strange problem that’s been bugging me for a few days and is starting to get a little annoying. When logging in it seems to log in fine but it’s not saving the session data itself to the session table. The session is there somewhere as when I do a dump of the $_SESSION array i can see the session data but as soon as I click on any link within the site it looses the session data.

The problem is definatly somewhere in there as I commented out the including of it and the requiring of it and stuck a session_start() right at the start the sessions worked fine. I don’t think it’s the update or insert quries as I swapped out the prepping placeholder for a known word and it inserted ok. I’ve tested it in isolation from the other scripts and it still shows the same behaviour, whatever the cause it’s probably stareing me right in the face.

<?php

class session implements SessionHandlerInterface {
    
    
      public function __construct($db) {
          $this->db = $db;
          session_set_save_handler($this);
          session_start();
      }
      
      public function open($path, $name) {
          $this->db;
          return true;
      }
      
      public function close() {
          $this->db = null;
          return true;
      }
      
  public function read($sess_id)
  {
    try
    {
      $sql = "SELECT sess_data FROM ue_user_session WHERE sess_id = :id";
      $stmt = $this->db->prepare($sql);
      $stmt->execute(array(':id'=>$sess_id));
      $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
      
      
    }
    catch (PDOException $e)
    { 
      error_log('Error reading the session data table in the session reading method.');
      error_log(' Query with error: '.$sql);
      error_log(' Reason given:'.$e->getMessage()."\
");
      return ''; 
    }
    if (count($res) > 0)
    {   
      return isset($res[0]['']) ? $res[0][''] : '';
    }
    else
    {
      return '';
    }
  }

public function write($sess_id, $data) {
    try {
        $sql = "SELECT sess_data FROM ue_user_session WHERE sess_id = :id";
        $stmt = $this->db->prepare($sql);
        $stmt->execute(array(':id'=>$sess_id));
        $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    catch (PDOException $e) {
        error_log('Error reading the session data table in the session writing method.');
        error_log(' Query with error: '.$sql);
        error_log(' Reason given:'.$e->getMessage()."\
");
        return false;
    }
    
    try {
        if (count($res) > 0) {
            $sql =     "
                    UPDATE
                        ue_user_session
                    SET
                        last_activity = NOW()
                         , sess_data = :sess_data
                    WHERE
                        sess_id = :id      
            ";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':sess_data', $data);
            $stmt->bindParam(':id', $sess_id);
        } else {
            $sql ="
                    INSERT INTO
                        ue_user_session
                    (
                          sess_id
                         , user               
                        , start
                        , last_activity
                        , expires
                        , sess_data
                    )
                        VALUES
                            (
                              :id
                            , 0  
                            , NOW()
                            , NOW()
                            , DATE_ADD(NOW(), INTERVAL 30 MINUTE)
                            , :sess_data
                            )                
            ";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':id', $sess_id);
            $stmt->bindParam(':sess_data', $data);
        }
        $res = $stmt->execute();
    }
    catch (PDOException $e) {
        error_log('Error writing to the session data table.');
        error_log('Query with error: '.$sql);
        error_log('Reason given:'.$e->getMessage()."\
");
        return false;
    }
    return true;
  }

  public function destroy($sess_id)
  {
    try
    {
      $sql = "DELETE FROM ue_user_session WHERE sess_id = :id";
      $stmt = $this->db->prepare($sql);
      $stmt->execute(array(':id'=>$sess_id)); 
    }
    catch (PDOException $e)
    {
      error_log('Error destroying the session.');
      error_log('Query with error: '.$sql);
      error_log('Reason given:'.$e->errorMessage()."\
");
      return false;
    }
    return true;
  }

  public function gc($ttl)
  {
    $end = time() - $ttl;
    try
    {
      $sql = "DELETE FROM ue_user_session WHERE last_activity <:end";
      $stmt = $this->db->prepare($sql);
      $stmt->execute(array(':id'=>$end));
    }
    catch (PDOException $e)
    {
      error_log('Error with the garbage collection method of the session class.');
      error_log('Query with error: '.$sql);
      error_log('Reason given:'.$e->getMessage());
      return false;
    }
    return true;
  }

  public function __destruct()
  {
    session_write_close();
  } 
}
?>
CREATE TABLE IF NOT EXISTS `ue_user_session` (
  `sess_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `user` int(11) NOT NULL,
  `start` datetime NOT NULL,
  `last_activity` datetime NOT NULL,
  `expires` datetime NOT NULL,
  `sess_data` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

I had a session problem which drove me potty. I have no idea if it’s the same thing but thought I’d mention it just in case. Basically it was caching. Browser caching. And only IE was so aggressively caching I was only seeing the problem in that. Anyway, if the browser is using its cache rather than accessing the server for it, obviously that stops any session stuff taking effect. Maybe that’s it? Maybe it’s not.

Nope, it’s not that, just tried in IE and Chrome, same session behaviour in both

From the manual:
Note:
The “write” handler is not executed until after the output stream is closed. Thus, output from debugging statements in the “write” handler will never be seen in the browser. If debugging output is necessary, it is suggested that the debug output be written to a file instead.

I’m going to have to set up a log file and log everything to that (there’s nothing wrong with the server as SMF which is also installed is saving it’s sessions perfectly fine to it’s own database)

That’s weird, the write function is being hit twice, the first time with the session data, the second time the session data is empty so it’s in effect wiping the session:

Debug: The session id is 8bmnpl0ie5oqoo6qcsibrrjm47<end>

Debug: The session data is username|s:12:“SpacePhoenix”;user_id|s:1:“1”;<end>

Debug: 1 sessions were found<end>
Debug: The session id is 8bmnpl0ie5oqoo6qcsibrrjm47<end>
Debug: The session data is <end>

Debug: 1 sessions were found<end>

This is a normal problem when you write a custom session handler and utilize a database.

The reason it does not save your session data is due to the database connection has been destroyed before it reach the “destroy session call” or in other words “writing the session data”.

Add this to you code, right after you define the custom session handler. Should fix your problems.

register_shutdown_function("session_write_close");

That didn’t make any difference, I popped into phpMyAdmin and done a repair of all the database’s tables and mysteriously it’s started working again, so maybe the table was corrupted.