So, after reading several posts — and then assessing the needs of our system, we have moved to a custom session handler for our websites. I initially read about this in PHP Security by Chris Shiflett and decided to research more. There are several benefits to this method, and the implementation is rather simple.

I have adapted some code and processes I have seen from other places to fit my needs.

This creates a session class, which handles the custom functions in the constructor. The only thing I am passing through is our PDO Database connection, which is already opened prior to the session. This eliminates the need to open/close the database, but true still needs to be returned.

This is in it’s initial phase and everything is working well so far. More tweaks will be made in the near future. Implementation is simple. We have a global configuration that calls:

session-file.php

  1. $sh = new session( $database_handle );

The database setup:

database.sql

  1. CREATE TABLE 'sessions' (
  2. 'id' varchar(32) NOT NULL default '',
  3. 'access' int(10) unsigned default NULL,
  4. 'data' text,
  5. PRIMARY KEY ('id')
  6. ) TYPE=InnoDB

And the sessions work as normal after that point.

class.session.php

  1. class session
  2. {
  3. private $_session;
  4. public $maxTime;
  5. private $db;
  6.  
  7. public function __construct(PDO $database)
  8. {
  9. $this->db = $database;
  10. $this->maxTime['access'] = time();
  11. $this->maxTime['gc'] = get_cfg_var('session.gc_maxlifetime');
  12. session_set_save_handler(array($this, '_open'),
  13. array($this, '_close'),
  14. array($this, '_read'),
  15. array($this, '_write'),
  16. array($this, '_destroy'),
  17. array($this, '_clean')
  18. );
  19. register_shutdown_function('session_write_close');
  20. session_start();
  21. }
  22.  
  23. public function _open()
  24. {
  25. return true;
  26. }
  27.  
  28. public function _close()
  29. {
  30. $this->_clean($this->maxTime['gc']);
  31. return true;
  32. }
  33.  
  34. public function _read($id)
  35. {
  36. $getData = $this->db->prepare("SELECT 'data'
  37. FROM 'sessions' AS 'Session'
  38. WHERE 'Session'.'id' = ?");
  39. $getData->bindParam(1, $id);
  40. $getData->execute();
  41.  
  42. $allData = $getData->fetch(PDO::FETCH_ASSOC);
  43. $totalData = count($allData);
  44. $hasData = (bool) $totalData >= 1;
  45.  
  46. return $hasData ? $allData['data'] : '';
  47. }
  48.  
  49. public function _write($id, $data)
  50. {
  51. $getData = $this->db->prepare("REPLACE INTO
  52. 'sessions'
  53. VALUES (?, ?, ?)");
  54. $getData->bindParam(1, $id);
  55. $getData->bindParam(2, $this->maxTime['access']);
  56. $getData->bindParam(3, $data);
  57. return $getData->execute();
  58. }
  59.  
  60. public function _destroy($id)
  61. {
  62. $getData = $this->db->prepare("DELETE FROM
  63. 'sessions'
  64. WHERE 'id' = ?");
  65. $getData->bindParam(1, $id);
  66. return $getData->execute();
  67. }
  68.  
  69. public function _clean($max)
  70. {
  71. $old = ($this->maxTime['access'] - $max);
  72.  
  73. $getData = $this->db->prepare("DELETE FROM
  74. 'sessions'
  75. WHERE 'access' < ?");
  76. $getData->bindParam(1, $old);
  77. return $getData->execute();
  78. }
  79. }

I initially had a few problems, but resolved those by turning off session.auto_start in php.ini and everything worked fine.

Resources

15 Comments Add your comment

  1. Hamidof June 3rd, 2006

    $this-&gt;_clean($this-&gt;maxTime[&#8217;gc&#8217;]);
    I think instead of this line you should just delete the current session, since its fresh and maybe its lifetime still remains!

    Cheers

  2. Hamidof June 4th, 2006

    Sorry, my mistake:)

  3. Nate Klaiber June 5th, 2006

    Hamidof,
    No problem :)

  4. iQ.mind January 26th, 2007

    Nice idea, Nate! Thanx!

  5. Evolic April 13th, 2007

    Why do you selecting records in _clean() function,
    instead of deleting them?

    Best regards

  6. Nate Klaiber April 14th, 2007

    RE: Evolic
    Wow, I am surprised I hadn't caught that yet - You are correct, it should be 'DELETE' instead of 'SELECT'.

    Thanks for bringing it to my attention - it has been fixed.

  7. Amit June 11th, 2007

    Two questions:

    1.) Close database doesn't really close database but just clears gc's maxlifetime.

    2.) What is the idea of playing with gc's maxlifetime and clearing it in first place?

  8. Amit June 11th, 2007

    EDIT: 1.) Close database doesn’t really close database but just clears gc’s maxlifetime. -- So what's happening there?

  9. Jeffery Fernandez July 22nd, 2007

    Does anyone know how I can retrieve the session data and utilise them for show list of online users ?

    In earlier versions of PHP I used to be able to see the session data as a serialised string. However in PHP 5.2.3 which I am currently using, I just see a long string. Does anyone know what format the string is in ?

  10. mark October 18th, 2007

    one question:
    how is the database connection passed over to the class?
    especially if you got (like me) an own mysql-class which is initialized at the beginning of the script - like this:

    $db = new db_MySQL;
    $db-&gt;Connect();
    $mver = $db-&gt;Version();

    Thanks! Mark

  11. Phil October 23rd, 2007

    Mark, if I read this right, the DB is passed into the class using the constructor.

    $db = new db_MySQL;
    $db-&gt;Connect();

    $session = new session( $db );

    You might have to tweak his DB code in the class to work with your DB object...

  12. Willy February 22nd, 2008

    Hello, this is my situation:

    I have a Login system.

    The session expires after 24 minutes. (1440=&gt;session.gc_maxlifetime)

    I need that after 30 minutes of inactivity the user be desactivated, then when logging in again, be redirected to the last page visited. (only the password required this time).

    But after 60 minutes of inactivity the user be disconnected at all. (username and pass required to log in again).

    The problem is that after 24 min. the session expires.

    How can i do?? It works on localhost, but not on-line.

    Thanks.

  13. Unreality June 19th, 2008

    I wouldn't call _clean everytime a session is closed. Too much performance needed. In php the gc probability is set to 1%. So every 100 Sessions the gc is called, that's more then enought

  14. TC April 23rd, 2009

    public $maxTime;

    What does this do? How can it be used?

  15. John Congdon March 11th, 2013

    One small critique as well, the SQL should be ENGINE=INNODB, not TYPE.

Leave a comment

Basic HTML is allowed (a href, strong, em, blockquote).