Part of the EllisLab Network
x
 
Create New Page
 View Previous Changes    ( Last updated by George Petsagourakis )

KNDB Session

Category:Library -> Session

Native Session makes good use of PHP’s native session handling abilities, but it does not allow the use of a database for session storage. KNDB Session (which stands for Kirilisa’s Native Database session) is a hybrid which is based upon Native Session (with some notable changes) but allows use of databases via PHP’s ability to store sessions in a DB.

Overview

- KNDB Session is based on Native Session but has database functionality built in.
- Should be compatible with Codeignitor version 1.54 and 1.6.
- Designed as drop-in replacement for CI’s bundled session library.
- Allows you to access values from single-dimensional arrays & objects stored in the session
- Config options and flash data are supported (with exception of session encryption).
- When using with a database, only the session_id is stored in a cookie. Any other data is stored in the database.
- When using without a database, only the session_id is stored in a cookie. Any other data is stored in a file on the server (as PHP does natively).
- KNDB Session is written for PHP5 but could be slightly altered to work with PHP4 (most notably the regenerate_id() method)

Differences between KNDB Session and Native Session

- Allows use of a database for session storage
- Keeps track of both sess_expiration and sess_time_to_update (in config) and thus distinguishes between session id needing regeneration and the session actually expiring (Native session considered sess_expiration as the time needed to regenerate a session id and never expires the session)
- Added method all_userdata() to get all session data: this method is in CI’s bundled session library but missing from Native Session
- Allows you to access values from single-dimensional arrays & objects stored in the session
- Made some changes to regenerate_id() method to shorten code and a fix potential bug there

Required Database Structure

CREATE TABLE sessions (                                                                                                                           
session_id varchar(32NOT NULL,                                                                                                                  
session_last_access int(10unsigned,                                                                                                             
session_data text,                                                                                                                                
PRIMARY KEY (session_id)                                                                                                                          
); 

Example Configuration (/system/application/config/config.php)

$config['sess_cookie_name']            'CISESSION';
$config['sess_expiration']            7200;
$config['sess_encrypt_cookie']        FALSE;
$config['sess_table_name']            'sessions';
$config['sess_match_ip']            TRUE;
$config['sess_match_useragent']        TRUE;
$config['sess_use_database']        TRUE;
$config['sess_time_to_update']        300

Usage

- Place the code in a file named Session.php in your /system/application/libraries/ directory.
- Use this lib as if you would CI’s bundled session library.

Changelog

March 31, 2008: Added ability for values to be retrieved from single-dimensional arrays in the session
April 10, 2008: Added ability for values to be retrieved from objects in the session

Code

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Code Igniter
 *
 * An open source application development framework for PHP 4.3.2 or newer
 *
 * @package     CodeIgniter
 * @author      Elise Bosse
 * @copyright   Copyright (c) 2008, E.Bosse
 * @license     http://www.codeignitor.com/user_guide/license.html
 * @link        http://www.codeigniter.com
 * @since       Version 1.2
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * Session class using native PHP session features and hardened against session fixation.
 * Non-database part is based upon Dariusz Debowczyk's Native session library with some updates.
 * The DB part makes use of PHP's session_set_save_handler() functionality
 * This library is written for PHP 5 but it could be altered a bit to make it work in PHP4
 *
 * @package     CodeIgniter
 * @subpackage  Libraries
 * @category    Sessions
 * @author      Elise Bosse
 * @link        http://www.codeigniter.com/user_guide/libraries/sessions.html
 * @class link  http://codeigniter.com/wiki/KNDB_Session/
 */

/**
 * If using a database, create the following database table and make sure config file is set properly
 *
 * CREATE TABLE sessions (
 * session_id varchar(32) NOT NULL,
 * session_last_access int(10) unsigned,
 * session_data text,
 * PRIMARY KEY (session_id)
 * );
*/

class CI_Session {

  
private $_lifetime
  private 
$_sess_id_ttl;
  private 
$_match_ip;
  private 
$_match_useragent;
  private 
$_sess_db;  
  private 
$_useDB;
  private 
$_sess_table;
  private 
$_flash_key 'flash'// prefix for "flash" variables (eg. flash:new:message)

  
function __construct()
  
{
    $this
->object =& get_instance();

    
// set config variables
    
$this->_lifetime $this->object->config->item('sess_expiration');
    
$this->_sess_id_ttl $this->object->config->item('sess_time_to_update');
    
$this->_match_ip $this->object->config->item('sess_match_ip');
    
$this->_match_useragent $this->object->config->item('sess_match_useragent');
    
$this->_useDB $this->object->config->item('sess_use_database');
    
$this->_sess_table $this->object->config->item('sess_table_name');

    
log_message('debug'"Session Class Initialized");

    
$this->_sess_run();
  
}

  
/**
   * Starts up the session system for current request
   */
  
function _sess_run()
  
{
    
// Set session table and register this object as the session handler, if using databases
    
if ($this->_useDB == TRUE{
      session_set_save_handler
(array(&$this"_open"), array(&$this"_close"), array(&$this"_read"),
                   array(&
$this"_write"),array(&$this"_destroy"),array(&$this"_gc"));
    
}

    session_start
();

    
// if no lifetime set in config, set to 2 years
    
if (!is_numeric($this->_lifetime) || $this->_lifetime <= 0{
      $this
->_lifetime = (60*60*24*365*2);
    
}

    
// if no session ID regeneration time set in config, set to 30 minutes
    
if (!is_numeric($this->_sess_id_ttl) || $this->_sess_id_ttl <= 0{
      $this
->_sess_id_ttl 1800;
    
}

    
// check if session has expired
    
if ($this->_session_expired()) {
      $this
->sess_destroy();
      return 
FALSE;
    
}

    
// match IP address if necessary
    
if ($this->_match_ip == TRUE{
      
if ($this->_ips_match() == FALSE{
    $this
->sess_destroy();
    return 
FALSE;
      
}
    }
    
    
// match user agent if necessary
    
if ($this->_match_useragent == TRUE{
      
if ($this->_useragents_match() == FALSE{
    $this
->sess_destroy();
    return 
FALSE;
      
}
    }

    
// regenerate session id if necessary
    // session data stays the same, but old session storage is destroyed
    
if ( $this->_sess_id_expired() ) {      
      $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();

    
// finally, set last access time to now
    
$this->set_userdata('sess_last_access'time());
  
}

  
/**
   * Checks if session has expired
   */
  
function _session_expired()
  
{
    
// if this is the first time coming in, initialize access time
    
if (!$this->userdata('sess_last_access')) {
      $this
->set_userdata('sess_last_access'time());
      return 
FALSE;
    
}

    $delta 
time() - $this->userdata('sess_last_access');

    if (
$delta  >=  $this->_lifetime {
      
return true
    
}

    
return false;
  
}

  
/**
   * Checks if stored IP matches current IP
   */
  
function _ips_match() {
    
// if this is the first time coming in, initialize IP address
    
if (!$this->userdata('sess_ip_address')) {
      $this
->set_userdata('sess_ip_address',  $this->object->input->ip_address());
      return 
TRUE;
    
}    

    
return $this->userdata('sess_ip_address') == $this->object->input->ip_address();
  
}

  
/**
   * Checks if stored user agent matches current user agent
   */
  
function _useragents_match() {
    
// if this is the first time coming in, initialize user agent
    
if (!$this->userdata('sess_useragent')) {
      $this
->set_userdata('sess_useragent'trim(substr($this->object->input->user_agent(), 050)));
      return 
TRUE;
    
}    

    
return $this->userdata('sess_useragent') == trim(substr($this->object->input->user_agent(), 050));
  
}


  
/**
   * Checks if session id needs regenerating
   */
  
function _sess_id_expired()
  
{
    
// if this is the first time coming in, initialize regenerated time
    
if (!$this->userdata('sess_last_regenerated')) {
      $this
->set_userdata('sess_last_regenerated'time());
      return 
false;
    
}

    $delta 
time() - $this->userdata('sess_last_regenerated');

    if ( 
$delta >=  $this->_sess_id_ttl 
      
return true;
    
}

    
return false;
  
}


  
/**
   * Regenerates session id
   */
  
function regenerate_id()
  
{
    
// regenerate session id and store it
    // $delete_old_session parameter works in PHP5 only! 
    
session_regenerate_id(TRUE);

    
// update the session generation time
    
$this->set_userdata('sess_last_regenerated'time());
  
}


  
/**
   * Destroys the session and erases session storage
   */
  
function sess_destroy()
  
{
    session_unset
();
    if ( isset( 
$_COOKIE[session_name()) )
      
{
    
//@@@ was having trouble just using setcookie() because it wasn't unsetting fast enough
    
unset($_COOKIE[session_name()]);
    
setcookie(session_name(), ''time()-42000'/'); //@@@
      
}

    session_destroy
();
  
}


  
/**                                                                                                                                             
   * returns the session id of the current session                                                                                                
   */
  
function get_sess_id() {
    
return session_id();
  
}


  
/**
   * Reads given session attribute value: single variable, element of single dimensional array, or property of object
   * I was kind of of two minds about whether the object bit should be implemented
   * so you can take out that logic if you wish
   */    
  
function userdata($item$subitem=null)
  
{
    
// this item is in an array
    
if ($subitem{
      
if ($subitem == 'session_id')//added for backward-compatibility                                                                           
        
return session_id();
      
else {
        
// array vs. object: handled differently
        
if (isset($_SESSION[$item])) {
          
if (is_array($_SESSION[$item])) return (!isset($_SESSION[$item][$subitem])) ? false $_SESSION[$item][$subitem];
          if (
is_object($_SESSION[$item])) return (!isset($_SESSION[$item]->$subitem)) ? false $_SESSION[$item]->$subitem;
          return 
false;
        
}
      }
    }

    
// this item is not in an array
    
else {
      
if($item == 'session_id')//added for backward-compatibility
        
return session_id();
      
else {
        
return ( ! isset($_SESSION[$item])) ? false $_SESSION[$item];
      
}
    }

  }

  
/**
   * Returns all session data
   */    
  
function all_userdata()
  
{
    
if (isset($_SESSION['session_id'])) //added for backward-compatibility
      
$_SESSION['session_id'session_id();
    
}
    
return $_SESSION;
  
}

  
/**
   * 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]);
      
}
      }
  }


/**
* 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) == && $parts[0] == $this->_flash_key)
      
{
        $this
->unset_userdata($name);
      
}
      }
  }




  
/************* DATABASE METHODS ***************/
  
function _open()
  
{
    
if ($this->_sess_db mysql_connect($this->object->db->hostname
                    
$this->object->db->username
                    
$this->object->db->password)) {
      
return mysql_select_db($this->object->db->database$this->_sess_db);
    
}
    
    
return FALSE;
  
}

  
function _close()
  
{
    
if ($this->_sess_db{
      
return mysql_close($this->_sess_db);
    
}

    
return TRUE;
  
}
  
  
function _read($id)
  
{
    $id 
mysql_real_escape_string($id);
    
    
$sql "SELECT session_data FROM $this->_sess_table WHERE session_id = '$id'";
    
    if (
$result mysql_query($sql$this->_sess_db)) {
      
if (mysql_num_rows($result)) {
    $record 
mysql_fetch_assoc($result);
    
    return 
$record['session_data'];
      
}
    }
    
    
return '';
  
}
  
  
function _write($id$data)
  
{
    $access 
time();

    
$id mysql_real_escape_string($id);
    
$access mysql_real_escape_string($access);
    
$data mysql_real_escape_string($data);
    
    
$sql "REPLACE INTO $this->_sess_table VALUES ('$id', '$access', '$data')";
    
    return 
mysql_query($sql$this->_sess_db);
  
}
  
  
function _destroy($id)
  
{
    $id 
mysql_real_escape_string($id);
    
    
$sql "DELETE FROM $this->_sess_table WHERE session_id = '$id'";

    return 
mysql_query($sql$this->_sess_db) or die("failed to delete<br>");
  
}
  
  
function _gc($max)
  
{
    $old 
time() - $max;
    
$old mysql_real_escape_string($old);
    
    
$sql "DELETE FROM $this->_sess_table WHERE session_last_access < '$old'";
    
    return 
mysql_query($sql$this->_sess_db);
  
}


}
?> 

Categories: