View Single Post
  #4 (permalink)  
Old July 7th, 2005, 05:39 PM
eggspencer eggspencer is offline
Registered User
 
Join Date: Jun 2005
Location: , , .
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I spent a bit of time this evening trying to resolve this problme on my machine. I am running php5 with mysql instead of the authors' beloved PostgreSQL. The following is the finalised code I have for dealing with sessions. It makes use of the PEAR DB as described in chapter 8.

The main stumbling block was the way the class updated the timestamps in the database. I have never used PGSQL, but the methods seemed to be inserting 'now()' to set a field to the current timestamp, as far as my experience goes my version of MySQL does not support this. On replacing this with date("YmdHis"), the page impression field is updated correctly in MySQL (of course you could just use the PHP built-in time() function and record that instead and change the cookie checking sql slightly).

Without this change the constructor was issuing a new session id every time as the statement it ran to check the cookie validity was failing due to the incorrect last_impression field. Now the class works as expected, maintaining the session and variables across pages.

One last thing I changed (as did the mlange) was the __set method, which now looks if a value has been set already then chooses to update or insert, otherwise we may end up with multiple rows with the same var name.

Here is the code...

<?php
require_once('class.Database.phpm');

  class UserSession {
    private $php_session_id;
    private $native_session_id;
    private $objDB;
    private $logged_in;
    private $user_id;
    private $session_timeout = 600; # 10 minute inactivity timeout
    private $session_lifespan = 3600; # 1 hour session duration

    public function __construct() {
      # Connect to database
      try {
        $this->objDB = Database::instance();
      } catch (Exception $e) {
        echo $e->getMessage();
        exit(1);
      }

      # Set up the handler
      session_set_save_handler(
          array(&$this, '_session_open_method'),
          array(&$this, '_session_close_method'),
          array(&$this, '_session_read_method'),
          array(&$this, '_session_write_method'),
          array(&$this, '_session_destroy_method'),
          array(&$this, '_session_gc_method')
      );
      # Check the cookie passed - if one is - if it looks wrong we'll scrub it right away
      $strUserAgent = $GLOBALS["HTTP_USER_AGENT"];
      if ($_COOKIE["PHPSESSID"]) {
       # Security and age check
       $this->php_session_id = $_COOKIE["PHPSESSID"];

       $stmt = "select id from user_session where ascii_session_id = '" . $this->php_session_id . "' AND ((now() - created) < ' " . $this->session_lifespan . " seconds') AND user_agent='" . $strUserAgent . "' AND ((now() - last_impression) <= '".$this->session_timeout." seconds' OR last_impression IS NULL)";

       $result = $this->objDB->select($stmt);
       if ($result->numRows() == 0) {
         # Set failed flag
         $failed = 1;
         # Delete from database - we do garbage cleanup at the same time
         $result = $this->objDB->delete('user_session', "(ascii_session_id = '". $this->php_session_id . "') OR ((now() - created) > " . $this->session_lifespan . ")");
         # Clean up stray session variables
         $session_ids_query = $this->objDB->select("SELECT id FROM user_session");
         $session_ids = $session_ids_query->getCol;

         if (count($session_ids) > 0) $result = $this->objDB->delete('session_variable', array("session_id NOT IN (" . implode(",", $session_ids) . ")"));
         # Get rid of this one... this will force PHP to give us another
         unset($_COOKIE["PHPSESSID"]);
       };
      };
      # Set the life time for the cookie
      session_set_cookie_params($this->session_lifespan);
      # Call the session_start method to get things started
      session_start();
    }

    public function Impress() {
      if ($this->native_session_id) {
        $result = $this->objDB->update('user_session', array('last_impression' => date("YmdHis")), "id = " . $this->native_session_id);
      };
    }

    public function IsLoggedIn() {
      return($this->logged_in);
    }

    public function GetUserID() {
      if ($this->logged_in) {
        return($this->user_id);
      } else {
        return(false);
      };
    }

    public function GetUserObject() {
      if ($this->logged_in) {
        if (class_exists("user")) {
          $objUser = new User($this->user_id);
          return($objUser);
        } else {
          return(false);
        };
      };
    }

    public function GetSessionIdentifier() {
      return($this->php_session_id);
    }

    public function Login($strUsername, $strPlainPassword) {
      $strMD5Password = md5($strPlainPassword);
      $result = $this->objDB->select("select id FROM user WHERE username = '$strUsername' AND md5_pw = '$strMD5Password'");

      if ($result->numRows() > 0) {
        $row = $result->fetchRow();
        $this->user_id = $row["id"];
        $this->logged_in = true;
        $result = $this->objDB->update('user_session', array('logged_in' => 1, 'user_id' => $this->user_id), "id = '" . $this->native_session_id . "'");
        return(true);
      } else {
        return(false);
      };
    }

    public function LogOut() {
      if ($this->logged_in == true) {
        $result = $this->objDB->update('user_session', array('logged_in' => 0, 'user_id' => 0), "id = '" . $this->native_session_id . "'");
        $this->logged_in = false;
        $this->user_id = 0;
        return(true);
      } else {
        return(false);
      };
    }

    public function __get($nm) {
      $result = $this->objDB->select("SELECT variable_value FROM session_variable WHERE session_id = " . $this->native_session_id . " AND variable_name = '" . $nm . "'");
      if ($result->numRows() > 0) {
        $row = $result->fetchRow();
        return(unserialize($row["variable_value"]));
      } else {
        return(false);
      };
    }


    public function __set($nm, $val) {
      $strSer = serialize($val);
      //if the variable has already been set, overwrite it
      $count = $this->objDB->getOne("SELECT COUNT(1) FROM session_variable WHERE variable_name = '" . $nm . "'");
      if ($count > 0){
        //update
        $result = $this->objDB->update('session_variable', array('variable_value' => $strSer), "variable_name = '" . $nm . "'");
      }
      else {
        //insert
        $result = $this->objDB->insert('session_variable', array('session_id' => $this->native_session_id, 'variable_name' => $nm, 'variable_value' => $strSer));
      }
    }

    private function _session_open_method($save_path, $session_name) {
      # Do nothing
      return(true);
    }

    public function _session_close_method() {
      return(true);
    }

    private function _session_read_method($id) {
      # We use this to determine whether or not our session actually exists.
      $strUserAgent = $GLOBALS["HTTP_USER_AGENT"];
      $this->php_session_id = $id;
      # Set failed flag to 1 for now
      $failed = 1;
      # See if this exists in the database or not.
      $result = $this->objDB->select("select id, logged_in, user_id from user_session where ascii_session_id LIKE '" . $id . "'");

      if ($result->numRows() > 0) {
       $row = $result->fetchRow();
       $this->native_session_id = $row["id"];
       if ($row["logged_in"]=="t") {
         $this->logged_in = true;
         $this->user_id = $row["user_id"];
       } else {
         $this->logged_in = false;
       };
      } else {
        $this->logged_in = false;
        # We need to create an entry in the database
        $result = $this->objDB->insert('user_session', array('ascii_session_id' => $id, 'logged_in' => 0, 'user_id' => 0, 'created' => date("YmdHis"), 'user_agent' => $strUserAgent));
        # Now get the true ID
        $result = $this->objDB->select("select id from user_session where ascii_session_id = '" . $id . "'");
        $row = $result->fetchRow();
        $this->native_session_id = $row["id"];
      };
      # Just return empty string
      return("");
    }

    public function _session_write_method($id, $sess_data) {
      return(true);
    }

    private function _session_destroy_method($id) {
      $result = pg_query("DELETE FROM \"user_session\" WHERE ascii_session_id = '$id'");
      return($result);
    }

    private function _session_gc_method($maxlifetime) {
      return(true);
    }


  }
?>

Sorry for the long post, hope this helps!

Ed