PHP

Mongo Nosql Session Handler (PHP)

This is the session handler I use in my websites which require nosql support. It work pretty much the same as a normal MySQL session handler.

I should probably explain some of the Mongo functions will look a little weird (ensureIndex()), do not worry I’m not using a different version of Mongo I just use an abstraction layer I made for my MVC Framework. The key functions such as the actual manipulation commands are the same as normal Mongo.

I also never delete my sessions there is a cronjob that does this for me all I do is set them inactive. There is a reason why I never delete a single session through this script. The main reason is session resurrection. To be able to use sessions as a reliable storage medium and have quickly depleating session times is to not do the conventional act of deleting a session but instead just mark it inactive until it is used again.

<?php

/*
 * This handles the user session and also stores information about the user that other users might need to see.
 */

class session {
	protected $mongo_db;
	
   	private $life_time;
   	private $session;

   function session($mongo) {
   	  
   	/*
   	 * Lets do some operations on our db to ensure its all setup
   	 */
   	  $this->mongo_db = $mongo->getCollection("sessions");
      $this->mongo_db->ensureIndex("session_id", array('session_id' => 1));
      
      // Read the maxlifetime setting from PHP
      $this->life_time = get_cfg_var("session.gc_maxlifetime");

      // Register this object as the session handler
      session_set_save_handler( 
        array( &$this, "open" ), 
        array( &$this, "close" ),
        array( &$this, "read" ),
        array( &$this, "write"),
        array( &$this, "destroy"),
        array( &$this, "gc" )
      );
	}

   function open( $save_path, $session_name ) {

      global $sess_save_path;

      $sess_save_path = $save_path;

      // Don't need to do anything. Just return TRUE.
      return true;

   }

   function close() {

      return true;

   }

   function read( $id ) {

      // Set empty result
      $data = '';

      // Fetch session data from the selected database
      $time = time();
      
      $result = $this->mongo_db->findOne(array("session_id" => $id));
		
	  if ($result) {
	  	$this->session = $result;
       	$data = $result['session_data'];
      }
      
      return $data;

   }

   function write( $id, $data ) {

      //Write details to session table             
      $time = time() + $this->life_time;
      $uid = $_SESSION['logged'] ? $_SESSION['uid'] : 0;
      
      if((array)$this->session){
      	$object = $this->session;
      	
      	$object["session_id"] = $id;
      	$object["user_id"] = $uid;
      	$object["session_data"] = $data;
      	$object["active"] = 1;
      	$object["expires"] = $time;
      }else{
      	$object = array("session_id" => $id, "user_id" => $_SESSION['uid'], "session_data" => $data, "active" => 1, "expires" => $time);
      }

      $this->mongo_db->save($object);
      
      return true;
   }

   function destroy( $id ) {
      
   	/*
   	 * This will remove the session from db when the user requests to logout
   	 */
   	
     $this->mongo_db->remove(array("session_id" => $id), true);
      
     return true;
   }

	function gc() {

		/*
		 * Garbage collection will remove all old and deceased sessions
		 */
		$time = time();
		
		/*
		 * XXX @todo: Make a script that can check whether the session expiry has passed nth time and delete it if it has 
		 */
		$this->mongo_db->update(array("expires" => array(':lt' => $time)), array(":set" => array("active" => 0)));
      
    	return true;

   	}

}

Advertisements

8 thoughts on “Mongo Nosql Session Handler (PHP)

  1. Common man. PHP 6 is almost out but you’re still using old school constructors (use __constructor instead of function session).
    Also you don’t need an amp in this case:
    array( &$this, “open” )
    because all objects are passed by reference since PHP5 (I think).
    Also I’m not quite sure why you can’t just do this in gc:
    $this->mongo_db->remove(array(“expires” => array(‘$lt’ => $time));
    Why do you need this ‘active’ flag?
    And of course since mongo loves wasting space whenever it cans instead of session_id, session_data and expires I’d just use s,d and e. Will save you couple KBs 🙂

    1. You’ll be surprised how useful function constructors can be. Take a MVC framework. SAay you need a global function for a controller but as usual to override the main constructor would mean to do:

      function __constructor(){
      parent::__constructor();
      }

      This can sometimes change the default required behaviour of some functions within the base controller class and can create undesired effects. To overcome this you can use a function constructor like so:

      function session(){
      //no need to call or potientially change parent behaviour 🙂
      }

      But yea your right in that particular class it is best to use a __constructor method to assign default behaviour.

      1. Any descent MVC will have a preDispatch and postDispatch callbacks for that purpose. You should avoid overriding default constructor behavior unless you really have to. And sometimes they would not even let you do it by making the constructor final.

    2. I don’t delete due to how my sessions work. If I delete a session the only way to ressurect session data would be to store it on user machine in the form of cookies and just trust that their cookies are right. To allow for the most accurate form of judging whether a user is online atm or not I have made session lifetime about 2 mins. This, from many trail and error methods is the most reliable way in PHP to understand if a user has lef the site or not. Your right I could use a physical program like facebook does to understand online users etc, and I might actually but yea I just made this one to work only with PHP.

      I think the amp is what wordpress actually did to my code, it seems to change it once every so oftern, thanks for the heads up.

      I instead run a cronjob that does the job after say a day of inactivity.

    3. “Any descent MVC will have a preDispatch and postDispatch callbacks for that purpose. You should avoid overriding default constructor behavior unless you really have to. And sometimes they would not even let you do it by making the constructor final.”

      Noted :), I’ll update my own knowledge to reflect. I did actually think of doing that way using callbacks in my own mvc at first but I dunno why I just chose to use function constructors instead. Though it looked cleaner.

    4. I did find a slight problem with your constructor idea. I recoded this today and found out php physically does not support a __constructor within the session handler class. Had to use function constructor instead. Not so old school after all :P. Found theres a lot more php core override classes that cannot take __constructor functions either.

      I knew there was a reason why I didn’t use __constructor originally.

  2. Because it’s not constructor, it’s construct 🙂

    <?php

    class SessionHandler
    {
    public function __construct($argument)
    {
    echo "[Constructed: {$argument}]" . '’;

    session_set_save_handler(
    array($this, ‘open’),
    array($this, ‘close’),
    array($this, ‘read’),
    array($this, ‘write’),
    array($this, ‘destroy’),
    array($this, ‘gc’)
    );
    }

    public function __destruct()
    {
    echo ‘[Destructed]’ . ”;
    }

    public function open() { echo ‘open’ . ”; }
    public function close() { echo ‘close’ . ”; }
    public function read($sid) { echo “read {$sid}”. ”; }
    public function write($sid, $data) { echo “write [{$sid}: {$data}]” . ”; }
    public function destroy() { echo ‘destroy’ . ”; }
    public function gc() { echo ‘gc’ . ”; }
    }

    // setup
    $sh = new SessionHandler(‘arg_data’);
    session_start();

    1. Oh oops I must have been high or something lol. Yea retried it and it worked dunno why I wrote __constructor(). Thanks 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s