Session Handling

By tye on Sep 26, 2005

This snippet is for PHP 5+.

This session class will allow you to create and manage sessions such as PHP Sessions but with more features. To use the class you first must include the file the class is contained in:

include("/path/to/sessions.php");

The class:

Inside the class are several constants which allow you to configure the class:
TABLE_NAME is the name of the MySQL table where the sessions will be stored
COOKIE_NAME is the name of the cookie the session will be stored in on the client's computer
COOKIE_PATH is the path the cookie will apply to

The MySQL table:

CREATE TABLE sessions (
sessionid varchar(40) NOT NULL default '',
time int(10) unsigned NOT NULL default '0',
expire int(10) unsigned NOT NULL default '0',
renew mediumint(8) unsigned NOT NULL default '0',
lastactive int(10) unsigned NOT NULL default '0',
ipaddress varchar(17) NOT NULL default '',
extra text NOT NULL,
PRIMARY KEY (sessionid),
KEY expire (expire)
);

This is the minimum structure you should have (userid and ipaddress are optional columns used as an example in the sessions class). Make sure the name of the table corresponds to the TABLE_NAME constant.

Creating a session:

Once you have included the sessions class file you can start the session using:

$session = new session();

This will create a session (or resume a session if the client already has one) and store its data in $session. For each session that is created a row will be put in the table configured above.

$session can then be used like this:

echo $session->column_name; to get a value from the session
$session->column_name = 'value'; to set a value in the session
column_name need not be a name of one of the columns in the table. If it is the name of a column in the table it will be stored in that column for the session's row otherwise it will be stored in the extra column. If you plan on using the value as part of any MySQL query it should be stored in its own column.

The $session->renew controls how long a session is renewed for everytime it is used. The renewal time is added to the current time to get the expiration time of the session. The expiration time of the session sets how long the session can be idle for before it is deleted. It is updated everytime the session is used. If $session->renew is zero then the cookie set on the client's computer will be 'temporary' (ie, deleted when the browser closes) and will have a renew time on the server of one hour. The $session->renew is non-zero then the cookie set on the client's computer will not be temporary (it will persist over browser sessions). A typical usage of this is that non-logged in users on a website will have a renew value of 0 (a temporary session) while logged in users will have a renew time of 90 days (so that they do not have to login everytime they visit the website).

Other members in the session class:
$session->sessionid a unique identifier for the session.
$session->time the time the session was created (as a unix timestamp)
$session->lastactive the last time the session was used (as a unix timestamp)
$session->expire the time the session will expire on the server (as a unix timepstamp)
$session->extra a serialized array of data stored in columns that are not in the MySQL table
$session->error Returns any errors thrown when the session object was initialized. If this is NULL then there were no errors and the session it represents is not new. If this is not NULL then there was an error and the session is new (an error does not mean the session object is not working).
$session->ipaddress the ipaddress of the client who created the session

You can arbitrarily create any variables you want, however all columns defined in the table will always exists as members of the object.

Public methods included in the session class:
$session->renew() renews the session (this is called automatically when the session object is created but you may want to call it on your own when you change the renew time)
$session->commit() saves changes to the session (this is called automatically when the session object is destroyed)
$session->delete() deletes the session from the database (you should also unset the $session object after calling this)

<?php

class session implements Iterator {
    // Constants + Configuration
    const TABLE_NAME = 'sessions'; // Name of MySQL table holding session data
    const COOKIE_NAME = 'test_session'; // Name of cookie holding session data
    const COOKIE_PATH = '/'; // Path cookie is stored for

    public $error = NULL; // Error message from __construct message

    public $columns = array();
    public $db_columns = array(); // Data stored in their own columns
    public $extra_columns = array(); // Data stored in extra column

    // Set a value in the session
    public function __set($var,$val) {
        $this->columns[$var] = $val;
    }

    // Get a value in the session
    public function __get($var) {
        return $this->columns[$var];
    }

    public function __unset($var) {
      if (in_array($var,$this->db_columns)) {
        $this->columns[$var] = NULL;
      } else {
        unset($this->columns[$var]);
      }
    }
    // Initialize the session
    public function __construct() {
          // Delete expired session
        mysql_query("DELETE FROM " . self::TABLE_NAME . " WHERE UNIX_TIMESTAMP() > expire");

        // Describe sessions table
        $s = mysql_query("DESCRIBE `" . self::TABLE_NAME. "`");
        while (list($col_name) = mysql_fetch_row($s)) {
            if ($col_name != 'extra') {
                $this->db_columns[] = $col_name;
            }
        }

        // Check for existing session (if an error occurs, create a new session)
        try {
            if (isset($_COOKIE[self::COOKIE_NAME])) {
                $sid = self::mysql_escape($_COOKIE[self::COOKIE_NAME]);
                $this->sessionid = $sid;

                // Get session data
                $row = @mysql_fetch_array(@mysql_query("SELECT * FROM `" . self::TABLE_NAME . "`
                WHERE sessionid='$sid' GROUP BY sessionid"),MYSQL_ASSOC);

                if (is_array($row)) {
                    foreach ($row as $var => $val) {
                        // Parse data in extra column
                        if ((string)$var == 'extra') {
                            $extra = @unserialize($val);
                            if (!is_array($extra)) throw new Exception('Error in extra data');
                            foreach ($extra as $e_var => $e_val) {
                                $this->extra_columns[] = $e_var;
                                $this->columns[$e_var] = $e_val;
                            }
                        } else {
                            $this->columns[$var] = $val;
                        }
                    }
                    // Renew session
                    $this->renew();
                } else throw new Exception('Session not found in database');
            } else throw new Exception('Client does not have a session');
        } catch (Exception $e) {
            // Set error message
            $this->error = $e->getMessage();

            // Create a new session for the client

            // Set this as a temporary session (browser will delete it when it is closed)
            $this->renew = 0;
            $this->time = time();
            $this->lastactive = time();
            /* * Note:
            New sessions will automatically expire in 5 minutes. This is to prevent
            clients who do not accept cookies from filling up the sessions table.
            The session will be set to expire further in the future the first time it
            is renewed.
            */
            $this->expire = time() + 300;

            // This can hold a userid and ip address in their own columns, as an example
            $this->ipaddress = self::mysql_escape($_SERVER['REMOTE_ADDR']);

            $sessionid = sha1(uniqid(rand(),1)); // Generate a random sessionid
            $this->sessionid = $sessionid;

            // Create an empty row in the sessions table
            mysql_query("INSERT INTO " . self::TABLE_NAME . "(sessionid) VALUES
            ('$sessionid')");

            // Store the cookie on the users computer
            setcookie(self::COOKIE_NAME,$sessionid,($this->renew ? $this->expire : NULL),self::COOKIE_PATH,
            self::domain());
        }
    }

    // Delete this session
    public function delete() {
        @mysql_query("DELETE FROM `" . self::TABLE_NAME . "` WHERE sessionid = '$this->sessionid'");
        $this->columns = NULL;
    }

    // Session variable is being destroyed
    public function __destruct() {
        // Commit changes to the session to the database
        $this->commit();
    }

    // Renew session
    public function renew() {
        // Renew for 3600s (1 hour) if the session is not a "permanent" one
        $expire = time() + ($this->renew ? $this->renew : 3600);
        $this->expire = $expire;
        $this->lastactive = time();

        // Set cookie with new expiration data
        setcookie(self::COOKIE_NAME,$this->sessionid,($this->renew ? $expire : NULL),self::COOKIE_PATH,
            self::domain());
    }

    // Commit changes made to the session
    public function commit() {
        // $this->columns will only be NULL if the session was deleted in which case we don;t
        // want to commit our changes
        if ($this->columns !== NULL) {
            $columns_ = $this->columns;
            foreach ($this->db_columns as $db_column) {
                $val = $columns_[$db_column];
                if ($val === NULL) {
                    $columns[] = "`$db_column` = NULL";
                } else {
                    $columns[] = "`$db_column` = '" . mysql_real_escape_string($val) . "'";
                }
                unset($columns_[$db_column]);

            }
            $ext_data = mysql_real_escape_string(serialize($columns_));
            $columns[] = "`extra` = '$ext_data'";

            $columns = implode(',',$columns);

            mysql_query("UPDATE " . self::TABLE_NAME . " SET $columns WHERE sessionid = '$this->sessionid'");
        }
    }

    /* ******** *
    Static Methods
    * ******** */

    // Returns a domain name used for storing the cookie
    // If requested host is an IP address, the entire address is returned
    // otherwise the top and second level domains are returned
    // ex, www.tye.ca will return tye.ca
    //     tye.ca will return tye.ca
    private static function domain() {
        $host = $_SERVER['HTTP_HOST'];
        if (is_numeric(substr($host,1,1))) {
            $domain = $host;
        } else {
            preg_match('/(?:.|^)([^.]*?.[^.]*?)$/',$host,$matches);
            $domain = $matches[1];
        }

        return (strlen($domain) ? $domain : FALSE);
    }

    // Escape Data for MySQL
    private static function mysql_escape($data) {
        if (get_magic_quotes_gpc()) {
            $data = stripslashes($data);
        }
        $data = mysql_real_escape_string($data);
        return $data;
    }

    // Iterator functions

    public function current() { return current($this->columns); }
    public function key() { return key($this->columns); }
    public function next() { return next($this->columns); }
    public function rewind() { return reset($this->columns); }
    public function valid() { return current($this->columns) !== FALSE; }
}

?>

Comments

Sign in to comment.
syncmaster   -  Jul 18, 2008

Hi,All
Good Work
My Problem Is When I M Implementing Above Code,
For Session Handling
My Form Is Become Active For Long Time?
I Have Set The Time 30 Second.
I Don\'t Know what Is Wrong With The Code?

I Copied The Same Code;;;;

Sync Master....

 Respond  
tye   -  Oct 04, 2005

It mostly has to do with the fact that the sessions are stored in the database. You can use them in queries etc.

 Respond  
Hawkee   -  Sep 26, 2005

Why not use the traditional $_SESSION management? I\'m just curious what added functionality yours has.

 Respond  
Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.