Custom PHP Session Class

PHP sessions are great for dealing with log in systems, tracking user activity, and allowing them to save data as they move around your site, like a shopping cart. Writing up what you need to run it though can be a bit painful, so that’s what lead me onto creating a custom session handling class in PHP.

The script handles sessions within a database to allow it to be more secure and analytically useful. It’s derived from an article by Richard Willars who I owe a lot to for the basis of this script. Anything that I don’t cover will be available on his website.

Here is the script:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
  Session class by Stephen McIntyre
  http://stevedecoded.com
*/
class Session
{
  private $alive = true;
  private $dbc = NULL;
 
  function __construct()
  {
    session_set_save_handler(
      array(&$this, 'open'),
      array(&$this, 'close'),
      array(&$this, 'read'),
      array(&$this, 'write'),
      array(&$this, 'destroy'),
      array(&$this, 'clean'));
 
    session_start();
  }
 
  function __destruct()
  {
    if($this->alive)
    {
      session_write_close();
      $this->alive = false;
    }
  }
 
  function delete()
  {
    if(ini_get('session.use_cookies'))
    {
      $params = session_get_cookie_params();
      setcookie(session_name(), '', time() - 42000,
        $params['path'], $params['domain'],
        $params['secure'], $params['httponly']
      );
    }
 
    session_destroy();
 
    $this->alive = false;
  }
 
  private function open()
  {    
    $this->dbc = new MYSQLi(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME)
      OR die('Could not connect to database.');
 
    return true;
  }
 
  private function close()
  {
    return $this->dbc->close();
  }
 
  private function read($sid)
  {
    $q = "SELECT `data` FROM `sessions` WHERE `id` = '".$this->dbc->real_escape_string($sid)."' LIMIT 1";
    $r = $this->dbc->query($q);
 
    if($r->num_rows == 1)
    {
      $fields = $r->fetch_assoc();
 
      return $fields['data'];
    }
    else
    {
      return '';
    }
  }
 
  private function write($sid, $data)
  {
    $q = "REPLACE INTO `sessions` (`id`, `data`) VALUES ('".$this->dbc->real_escape_string($sid)."', '".$this->dbc->real_escape_string($data)."')";
    $this->dbc->query($q);
 
    return $this->dbc->affected_rows;
  }
 
  private function destroy($sid)
  {
    $q = "DELETE FROM `sessions` WHERE `id` = '".$this->dbc->real_escape_string($sid)."'"; 
    $this->dbc->query($q);
 
    $_SESSION = array();
 
    return $this->dbc->affected_rows;
  }
 
  private function clean($expire)
  {
    $q = "DELETE FROM `sessions` WHERE DATE_ADD(`last_accessed`, INTERVAL ".(int) $expire." SECOND) < NOW()"; 
    $this->dbc->query($q);
 
    return $this->dbc->affected_rows;
  }
}

Before you try to use the class you have to set up your database. I have used exactly the same database setup as Richard here, but can easily be expanded into using user IDs and shopping carts as I mentioned before.

1
2
3
4
5
6
CREATE TABLE `sessions` (
  `id` CHAR(32) NOT NULL,
  `data` longtext NOT NULL,
  `last_accessed` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

If you use phpMyAdmin it’s as simple as adding a database as you normally would and pasting the above into the SQL query box.

For the class to connect to the database, you also need to create some definitions in PHP.

1
2
3
4
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', 'penguin');
define('DB_HOST', 'localhost');

This should be placed at the top of your PHP file, replacing the appropriate fields with your own database information.

That’s it set up. All you have to do now is call it in one line.

1
$session = new Session();

And set our session data with the $_SESSION variable as usual.

1
2
$session = new Session();
$_SESSION['user_id'] = 1;

The user ID above will then be stored inside the “data” field in your database, in a serialised format that the class will be able to parse.

Since it’s handled from a class, the variable will also call session_write_close(); by itself when the page ends, so no need to call this yourself. If you want to end it early though, you can always call the destructor manually.

1
2
3
$session = new Session();
// ... extra code ...
$session->__destruct();

The session can also be deleted (like when logging out) and to do this we just call the delete method.

1
2
3
$session = new Session();
// ... extra code ...
$session->delete();

So there you have it, a simple session handling class you can use for any web project.

Also, if you do use this in your applications, please be kind enough to leave the extra header text at the top.

Thanks :)


30 thoughts on “Custom PHP Session Class

  1. I like the helpful information you provide in your articles. I’ll bookmark your blog and check again here frequently. I am quite sure I’ll learn a lot of new stuff right here! Best of luck for the next!

    1. Garbage collection is really more of a software term rather than in web languages, as PHP just dumps everything at the end of the pass and doesn’t need to manage its memory after the page has been loaded.

      The clean function does actually get rid of old data from the database though, it removes any sessions outside of the expiration date. What it does not do is clean up old data from a user’s session, and this would be handled dependent on your own project as caching times of certain bits of session data would be inconsistent through most sites. The algorithms for filtering these would be much more intensive too.

      On php.net this parameter *is* actually named “gc” for “garbage collection”, and by passing in an expiration date is intended to be used in this way.

      Hope this answered your question. Take care. :)

  2. Hi,
    I like this class and i’m planning to use it. Is there any reason to use innoDB instead mysql? The reason i’m asking because with only one active session my phpadmin says “InnoDB free: 5120 kB” which is 5MB. What if i have like 1000 sessions?

    Thanks

    1. It’s the recommended default by MySQL. I’d never really looked into it before but I was advised that it’s your best database engine to use with this. There are some detailed articles all over the internet, but basically InnoDB will handle most of the simple tasks more efficiently than others.

  3. Hi,

    Thanks for this class.

    I would like to use this for my login page and be able to keep all the data in the database table throughout a user’s active session. For example, when a user logs in I want to set:

    $session = new Session();
    $_SESSION[‘logged_in’] = 1;
    $_SESSION[‘user_id’] = 1;

    This works fine and also writes to the database fine, however, if I go to another page and try reading from the session I lose the data, for example:

    $session = new Session();
    echo $_SESSION[‘user_id’];

    This outputs nothing and I also lose the data in the database table.

    Can you help and give me an example of how to set and use across multiple pages?

    Thanks!

    1. I ran this script again with your example and it definitely works for me across multiple pages.

      1. Is the row being removed from your database?: Make sure you’re not calling $session->delete().
      2. Is the data field being cleared to empty in the database row?: Make sure you’re not calling $_SESSION = array().
      3. Is the data staying in the table, but the values are being changed?: Check you’re not setting $_SESSION['logged_in'] etc. to something different on page two.

      I would check the three cases above for anything familiar.

      1. Hi,

        i have the same problem and in my case, the data field is being cleared as soon as :

        $session = new Session();

        is called. For the rest, everything is exactly as above.

        Is there any server setting that has to change? I’m pretty much pulling my hair out.

        Thanks for any help.

  4. Is there a way to completely dump all sessions? I tried doing it outside of your class (ie DELETE FROM `sessions` WHERE `id` != ”), but it simply doesn’t work. I’ve also tried adding a dump() function to your class using ($r = $this->dbc->query(“DELETE FROM `sessions` WHERE `id` != ””)) but still nothing. Any ideas?

    1. In SQL, the “does not equal” syntax is <> and not != perhaps this is your problem.

  5. Good stuff, it’s a good base class to start with and modify until something custom you really need(if need be of course).

    The main change I did when using it is convert it so that it’s a singleton class, this way no matter at which depth of the application you’re writing, you always get the same session object throughout the lifetime of that one script. That way it makes it easier to not have to pass around the session object the whole time.

    Just thought I’d throw that in there.

    Thanks again

  6. One note. On the clean function, you’re using the column sesh_last_accessed rather than just last_accessed

  7. What is $id used for – I see it referenced in the read method, but can not see it actually being used.

    1. It was a placeholder for the session ID should you want to add a function to fetch it outside of the class.

      It is however currently redundant, so I’ve removed it from the class. Thanks.

  8. I would like to you this, but I am curently using MySQL instead of MySQLi. I also use a database handler class. Is it wise to rewrite your class for this purpuse, or is it better to switch to MySQLi ?

  9. Hello there, I am trying out your script and it works fine except when I look on my server for the actual session file it’s no where to be found.

    Without using your class running a basic php session will create the session file as expected.

    Is this class disabling a physical file on the server ?

  10. Hi Stephen,

    Any idea if the post on Richard Willars site can be found else where? It seems to have been removed.

    Thanks,
    Jonny

  11. Hi,

    I’m learning PHP now and I wanted to build a custom session handler. Came across your code which seems to be understandable. However, I do have one question that I’m hoping you can explain to me.

    I did a test page to see how it’s working. This is the code I wrote. The $objDbHandler contains database connection settings.

    $session = new Session($objDbHandler);
    $_SESSION[‘user_id’] = 1;
    $_SESSION[‘name’] = “JB”;
    echo $_SESSION[‘name’];

    In read and write functions, I wrote an echo just to see how they execute. The write function is being called AFTER the echo above. And the write query is not executing for some reason. The affected rows is returning 0 even though the query is correct (working when executed manually).

    1. nevermind. I figured it out. For some weird reason the column names with single quotes doesn’t work. It seems to work fine without the single quotes.

      Thanks for the nice coding. Appreciate it.

  12. Just a thought, wouldn’t it be wiser to use a Memory Storage Engine instead of InnoDB for the sessions table? its a lot quicker and the information is not really crash-sensitive since they are just user sessions.

  13. Hey Stephen,
    the code looks great. Is there anything you should watch out for when using this class with regards to SSL encryption?
    We are saving some variables to session on a SSL encrypted page and are not able to call them on a non SSL encrypted page afterwards. Do you have any idea what may cause this?
    Many thanks for any help,
    Daan
    PS: I am pretty new to PHP, so I hope I am not making a fool out of myself asking this 😉

  14. Would I have to call the $session = new Session(); in top of every page like when I usually write “session_start();” in top of every page? I just don’t understand it because would would mean that I am creating a new class session every time.. Will it then still keep the session going? And what if I would like the regenerate session id = true.. where should I put that?

    1. Yes you would include new Session() in all pages which require the session. I believe you are creating a new session every time, but filling the session with saved data pulled from the database. Then its up to you to check/update/validate the session data via your own approach.

Comments are closed.