Chill out and play some games on The Flash Core

Mar
18

Custom PHP Session Class

in Blog, PHP with 19 Comments | Trackback

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://stephenmcintyre.net
*/
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 :)

19 Responses to “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!

  2. chris says:

    Shouldn’t there be a garbage collector in there somewhere instead of clean?

    • Stephen McIntyre says:

      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. :)

  3. Sabato says:

    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

    • Stephen McIntyre says:

      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.

  4. Andy says:

    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!

    • Stephen McIntyre says:

      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.

  5. Wes says:

    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?

  6. Freddy says:

    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

  7. Freddy says:

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

  8. Jared says:

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

    • Stephen McIntyre says:

      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.

  9. gersom says:

    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 ?

Leave a Reply

Trackbacks

  1. JD Angel News » Eastsider Karan Brar, age 11, has key role in 'Wimpy Kid'
  2. Video-Tutorial: Für Einsteiger PHP + Sessions : Video Tutorials Web 2.0
  3. How to Display a User’s Visited Web Pages « Stephen McIntyre