Philsbury

Posted in Web dev on 3rd August 2009

Code Igniter Sessions

EDIT: I've posted an update view on this article called Codeigniter Sessions: Revisited which I feel gives a better way to manage any Internet Explorer related issues. No additional code required!

I was using the Session's in Code Igniter recently to create a user signup/login system. Everything was work perfectly until I tested on Internet Explorer.

I went on a hunt and found a solution, create a new file in application/library the call it Session.php, then load it as you would with the native Codeigniter library.

$this->load->library('session');

Here's the source code for the new library:

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
* Session class using native PHP session features and hardened against session fixation.
*
* @package     CodeIgniter
* @subpackage  Libraries
* @category    Sessions
* @author      Dariusz Debowczyk
* @link        http://www.codeigniter.com/user_guide/libraries/sessions.html
*/
class CI_Session {

	var $flash_key = 'flash'; // prefix for "flash" variables (eg. flash:new:message)

	function CI_Session()
	{
		$this->object =& get_instance();
		log_message('debug', "Native_session Class Initialized");
		$this->_sess_run();
	}

	/**
    * Regenerates session id
    */
	function regenerate_id()
	{
		// copy old session data, including its id
		$old_session_id = session_id();
		$old_session_data = $_SESSION;

		// regenerate session id and store it
		session_regenerate_id();
		$new_session_id = session_id();

		// switch to the old session and destroy its storage
		session_id($old_session_id);
		session_destroy();

		// switch back to the new session id and send the cookie
		session_id($new_session_id);
		session_start();

		// restore the old session data into the new session
		$_SESSION = $old_session_data;

		// update the session creation time
		$_SESSION['regenerated'] = time();

		// session_write_close() patch based on this thread
		// http://www.codeigniter.com/forums/viewthread/1624/
		// there is a question mark ?? as to side affects

		// end the current session and store session data.
		session_write_close();
	}

	/**
    * Destroys the session and erases session storage
    */
	function destroy()
	{
		unset($_SESSION);
		if ( isset( $_COOKIE[session_name()] ) )
		{
			setcookie(session_name(), '', time()-42000, '/');
		}
		session_destroy();
	}

	/**
    * Reads given session attribute value
    */    
	function userdata($item)
	{
		if($item == 'session_id'){ //added for backward-compatibility
			return session_id();
		}else{
			return ( ! isset($_SESSION[$item])) ? false : $_SESSION[$item];
		}
	}

	/**
    * Sets session attributes to the given values
    */
	function set_userdata($newdata = array(), $newval = '')
	{
		if (is_string($newdata))
		{
			$newdata = array($newdata => $newval);
		}

		if (count($newdata) > 0)
		{
			foreach ($newdata as $key => $val)
			{
				$_SESSION[$key] = $val;
			}
		}
	}

	/**
    * Erases given session attributes
    */
	function unset_userdata($newdata = array())
	{
		if (is_string($newdata))
		{
			$newdata = array($newdata => '');
		}

		if (count($newdata) > 0)
		{
			foreach ($newdata as $key => $val)
			{
				unset($_SESSION[$key]);
			}
		}
	}

	/**
    * Starts up the session system for current request
    */
	function _sess_run()
	{
		session_start();

		$session_id_ttl = $this->object->config->item('sess_expiration');

		if (is_numeric($session_id_ttl))
		{
			if ($session_id_ttl > 0)
			{
				$this->session_id_ttl = $this->object->config->item('sess_expiration');
			}
			else
			{
				$this->session_id_ttl = (60*60*24*365*2);
			}
		}

		// check if session id needs regeneration
		if ( $this->_session_id_expired() )
		{
			// regenerate session id (session data stays the
			// same, but old session storage is destroyed)
			$this->regenerate_id();
		}

		// delete old flashdata (from last request)
		$this->_flashdata_sweep();

		// mark all new flashdata as old (data will be deleted before next request)
		$this->_flashdata_mark();
	}

	/**
    * Checks if session has expired
    */
	function _session_id_expired()
	{
		if ( !isset( $_SESSION['regenerated'] ) )
		{
			$_SESSION['regenerated'] = time();
			return false;
		}

		$expiry_time = time() - $this->session_id_ttl;

		if ( $_SESSION['regenerated'] <=  $expiry_time )
		{
			return true;
		}

		return false;
	}

	/**
    * Sets "flash" data which will be available only in next request (then it will
    * be deleted from session). You can use it to implement "Save succeeded" messages
    * after redirect.
    */
	function set_flashdata($key, $value)
	{
		$flash_key = $this->flash_key.':new:'.$key;
		$this->set_userdata($flash_key, $value);
	}

	/**
    * Keeps existing "flash" data available to next request.
    */
	function keep_flashdata($key)
	{
		$old_flash_key = $this->flash_key.':old:'.$key;
		$value = $this->userdata($old_flash_key);

		$new_flash_key = $this->flash_key.':new:'.$key;
		$this->set_userdata($new_flash_key, $value);
	}

	/**
    * Returns "flash" data for the given key.
    */
	function flashdata($key)
	{
		$flash_key = $this->flash_key.':old:'.$key;
		return $this->userdata($flash_key);
	}

	/**
    * PRIVATE: Internal method - marks "flash" session attributes as 'old'
    */
	function _flashdata_mark()
	{
		foreach ($_SESSION as $name => $value)
		{
			$parts = explode(':new:', $name);
			if (is_array($parts) && count($parts) == 2)
			{
				$new_name = $this->flash_key.':old:'.$parts[1];
				$this->set_userdata($new_name, $value);
				$this->unset_userdata($name);
			}
		}
	}

	/**
    * PRIVATE: Internal method - removes "flash" session marked as 'old'
    */
	function _flashdata_sweep()
	{
		foreach ($_SESSION as $name => $value)
		{
			$parts = explode(':old:', $name);
			if (is_array($parts) && count($parts) == 2 && $parts[0] == $this->flash_key)
			{
				$this->unset_userdata($name);
			}
		}
	}
}
?>

Hope it helps! :)

All credit to Dariusz Debowczyk, who created it!

Discussion

  • Jakub

    11th September 2009

    So just to confirm, this Session.php file should be placed in /system/application/libraries/
    And it will be utilized without a 'load library' function? Does this append to the /system/libraries/Session.php file?
    Can you confirm?

    • Phil

      11th September 2009

      Hi Jakub, you'll still need to load the class as you would normally either through the autoload config or $this->load->library('session');

      • Jakub

        11th September 2009

        Thanks, Phil, thats what I figured, I would suggest you probably update your post to be a bit more concise IMHO. Thanks again! I ran into this issue myself with IE8 not holding sessions correctly for CI.

  • Matthew

    18th November 2009

    Errors in your instructions: In your post, you tell us to name the new library file, "Session.php" but then line 1 of your instructions says: "$this->load->library('sessions');" notice you made it plural when it started out singular. I also had to rename the method "destroy" to "sess_destroy" and do some other stuff related to flashdata. I can send you my updated version if you like. It works with 1.7.2 verion of CI.

    • Phil

      18th November 2009

      Matthew, you're absolutely right, it should have been singular. I'd spotted this week that the session_destroy wasn't working in 1.7.2, but had worked around it using unset_session

      • Matthew

        19th November 2009

        Phil, this may be of interest to you. I started a little war on the CI forums about CI's broken session handling library. http://codeigniter.com/forums/viewthread/130577/ http://codeigniter.com/wiki/Dariusz_Debowczyk%27s_Session_Class/

  • Jason

    12th March 2010

    Hi Phil, Thanks for this 'unofficial patch' for CI, lol. I'm wondering, all the other tutorials I've found mention to not close out the PHP tag. Are libraries different? I'm pretty new to CI. :D

  • ispuk

    6th November 2010

    hi i found this code really really good.

    can you tell me ,about ajax request?

    which config data should i use with this library?

    $[sess_expiration_datetime] =
    [sess_time_to_update] = ?

    i'm not using db to store sessions ;)

    thanks for codeigniter supporting you hav edone a really BIG job :))

  • luis carlos caƱaveral

    28th September 2012

    Phil, thanks for your code....this patch resolve my problems

  • Verjs

    4th January 2013

    thanks a lot for this code.. it really helps a lot. my headache is now gone..

Comments have been disabled on this post