Page 1 of 1

session_set_save_handler()

Posted: Tue Jul 13, 2004 6:58 am
by Sat
I want to save all my session in the database.
Function session_set_save_handler() provides such functionality. But I can't understand... in which format I receive and must return $sess_data in read and write functions. And how can I use this format. For example. Table consists of 3 columns: sid, param and value.

in sid I want to store session_id (varchar(255))
in param ... - name of session variable (varchar(255))
and value - it's value... (text)

string = variable.

Write me a sample code for function write() pls.

Posted: Tue Jul 13, 2004 9:51 am
by hedge
I believe the parameter $sess_data includes the data of your session already serialized. I just wrote this string to the db and retrieved the same string back.

I guess if you have a need to be able to see each var then you could unserialize it and serialize it again when requested.

Posted: Tue Jul 13, 2004 9:53 am
by feyd
This is something I helped write a few weeks ago..

Code: Select all

<?php 

highlight_file(__FILE__);

if(!defined('_DEBUG_')) define('_DEBUG_',0);

if(_DEBUG_) header('Content-type: text/plain');


/* Create new object of class */ 
$ses_class = new session();

/* Change the save_handler to use the class functions */ 
session_set_save_handler (array(&$ses_class, '_open'), 
                          array(&$ses_class, '_close'), 
                          array(&$ses_class, '_read'), 
                          array(&$ses_class, '_write'), 
                          array(&$ses_class, '_destroy'), 
                          array(&$ses_class, '_gc')); 

/* Start the session */ 
session_start(); 

function sessDataUnpack($sessData) 
{ 
	if(!is_string($sessData)) 
		return false; 
	        
	$data = preg_split('#([\w\d]+?)\|#',$sessData,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 

	$flip = true; 
	$sessData = array(); 
	foreach($data as $bit) 
	{ 
		if($flip) 
			$key = $bit; 
		else 
			$sessData[$key] = unserialize($bit); 
		$flip = !$flip; 
	}
	
	if(_DEBUG_)
	echo __LINE__."\n".print_r($data,true);
	
	return $sessData; 
} 
	
function arrayPull(&$array, $key, $leave = false) 
{ 
	if(!is_array($array)) 
		return false; 
	if(!is_integer($key) && !is_string($key)) 
		return false; 
	if(!isset($array[$key])) 
		return ''; 
	    
	$ret = $array[$key]; 
	if(!$leave) 
		unset($array[$key]); 
	    
	return $ret; 
} 
	
function sessDataPack($array) 
{ 
	if(!is_array($array)) 
		return false; 
	    
	$out = ''; 
	foreach($array as $key => $data) 
	{ 
		$out .= $key . '|' . serialize($data); 
	} 
	    
	return $out; 
} 
	
function sessDataRepack(&$sessionData, $keys, &$extracted, $leave = false) 
{ 
	if(!is_string($sessionData)) 
		return false; 
	if(!is_string($keys) && !is_array($keys)) 
		return false; 
	    
	$data = sessDataUnpack($sessionData); 
	    
	if(!is_array($data)) 
		return false; 
	    
	if(is_array($keys)) 
	{ 
		$extracted = array(); 
		foreach($keys as $key) 
		{ 
			$extracted[] = arrayPull($data,$key,$leave); 
		} 
		$ret = sizeof($extracted); 
	} 
	else 
	{ 
		$extracted = arrayPull($data,$keys,$leave); 
		$ret = 0; 
	}
	
	if(_DEBUG_)
	echo __LINE__."\n".print_r($data,true);
	    
	if(_DEBUG_)
	echo __LINE__."\n".print_r($extracted,true);
	    
	$sessionData = sessDataPack($data); 
	    
	if(_DEBUG_)
	echo __LINE__."\n".$sessionData."\n";
	    
	return $ret; 
} 

class session 
{ 
	/* Define the mysql table you wish to use with 
		this class, this table MUST exist. */ 
	var $ses_table = "sessions"; 

	/* Change to 'Y' if you want to connect to a db in 
		the _open function */ 
	var $db_con = "Y"; 

	/* Configure the info to connect to MySQL, only required 
		if $db_con is set to 'Y' */ 
	var $db_host = "localhost"; 
	var $db_user = "************"; 
	var $db_pass = "************"; 
	var $db_dbase = "*************"; 
	
	var $mysql_connect = null;
	var $mysql_db = null;

	/* Create a connection to a database */ 
	function db_connect() { 
		$this->mysql_connect = mysql_connect ($this->db_host,$this->db_user,$this->db_pass) or die(mysql_error());
		$this->mysql_db = mysql_select_db ($this->db_dbase) or die(mysql_error()); 

		if (!$mysql_connect || !$mysql_db) { 
			return FALSE; 
		} else { 
			return TRUE; 
		} 
	} 

	/* Open session, if you have your own db connection 
		code, put it in here! */ 
	function _open($path, $name) { 
		if ($this->db_con == "Y") { 
			$this->db_connect(); 
		} 

		if(_DEBUG_)
	    echo __LINE__."\n";

		return TRUE; 
	} 

	/* Close session */ 
	function _close() { 
		/* This is used for a manual call of the 
			session gc function */ 

		if(_DEBUG_)
	    echo __LINE__."\n";
	    
	    if($this->mysql_connect !== null)
			mysql_close($this->mysql_connect);

		//$this->_gc(0); 
		return TRUE; 
	} 

	/* Read session data from database */ 
	function _read($ses_id) { 

		if(_DEBUG_)
	    echo __LINE__."\n";

		$session_sql = "SELECT * FROM " . $this->ses_table 
						. " WHERE ses_id = '$ses_id'"; 
		$session_res = mysql_query($session_sql) or die(mysql_error()); 

		if(_DEBUG_)
	    echo __LINE__."\n";

		if (!$session_res) { 
			return ''; 
		} 

		if(_DEBUG_)
	    echo __LINE__."\n";

		$session_num = mysql_num_rows ($session_res); 
		if ($session_num > 0) { 
			$session_row = mysql_fetch_assoc ($session_res); 
			$ses_data = $session_row["ses_value"]; 
			return $ses_data; 
		} else { 
			return ''; 
		} 
	} 

	/* Write new data to database */ 
	function _write($ses_id, $data) { 

		if(_DEBUG_)
	    echo __LINE__."\n".$data."\n";

		if(sessDataRepack($data,array('user','powerlevel'),$ret) == 2) 
			list($us,$pl) = $ret; 
		else 
			$us = $pl = ''; 

		if(_DEBUG_)
	    echo __LINE__."\n".print_r($ret,true);

		$session_sql = "UPDATE " . $this->ses_table 
						. " SET ses_time = UNIX_TIMESTAMP(NOW()), ses_value='$data', ses_user='$us', ses_pow='$pl' WHERE ses_id='$ses_id'"; 
		$session_res = mysql_query ($session_sql) or die(mysql_error()); 

		if(_DEBUG_)
	    echo __LINE__."\n".$session_sql."\n";

		if (!$session_res) { 
			return FALSE; 
		} 

		if(_DEBUG_)
	    echo __LINE__."\n";

		if (mysql_affected_rows ()) { 
			return TRUE; 
		} 

		if(_DEBUG_)
	    echo __LINE__."\n";

		$session_sql = "INSERT INTO " . $this->ses_table 
						. " (ses_id, ses_time, ses_start, ses_value, ses_user, ses_pow)" 
						. " VALUES ('$ses_id', UNIX_TIMESTAMP(NOW()), UNIX_TIMESTAMP(NOW()), '$data', '$us', '$pl')"; 
		$session_res = mysql_query ($session_sql);

		if(_DEBUG_)
	    echo __LINE__."\n".$session_sql."\n";

		$match = "#^Duplicate entry '$ses_id' for key \d+$#";
		if (!$session_res && !preg_match($match,mysql_error())) {
			if(_DEBUG_)
			echo __LINE__."\n".$match."\n".mysql_error()."\n";
			return FALSE; 
		} else { 
			if(_DEBUG_)
			echo __LINE__."\n";
			return TRUE; 
		} 
	} 

	/* Destroy session record in database */ 
	function _destroy($ses_id) { 

		if(_DEBUG_)
	    echo __LINE__."\n";

		$session_sql = "DELETE FROM " . $this->ses_table 
						. " WHERE ses_id = '$ses_id'"; 
		$session_res = mysql_query ($session_sql) or die(mysql_error());

		if(_DEBUG_)
	    echo __LINE__."\n".$session_sql."\n";

		if (!$session_res) { 
			return FALSE; 
		}         else { 
			return TRUE; 
		} 
	} 

	/* Garbage collection, deletes old sessions */ 
	function _gc($life) { 

		if(_DEBUG_)
	    echo __LINE__."\n";

		$session_sql = "DELETE FROM " . $this->ses_table 
						. " WHERE ses_time < UNIX_TIMESTAMP(NOW() - $life)"; 
		$session_res = mysql_query ($session_sql) or die(mysql_error()); 

		if(_DEBUG_)
	    echo __LINE__."\n".$session_sql."\n";

		if (!$session_res) { 
			return FALSE; 
		}         else { 
			return TRUE; 
		} 
	} 
}

$_SESSION['user'] = 'ted';
$_SESSION['powerlevel'] = 20;
$_SESSION['test'] = array('123',456,789=>'012','345'=>678);

?>

Posted: Wed Jul 14, 2004 3:12 am
by Sat
Thanks

Posted: Sun Mar 27, 2005 10:26 pm
by SystemWisdom
Can you give me an explanation as to how you are packing the data, and why? I really didn't want to logically analyze your pack routines.. I was hoping instead that you wouldn't mind explaining it??

I currently use a DB to store my sessions as well, for security reasons, but does packing the data like you have done add more security (other than obfuscation)?? Cause if so, I would then really like to know how you did it (logically), so I may implement it into my own scripts as well!!

Thx! :D

Posted: Mon Mar 28, 2005 12:42 am
by feyd
the packing and unpacking is done in accordance with how the built-in session system does the file based ones. It expects the data in that format. At least it did when I was working with it.

Posted: Thu Apr 28, 2005 10:36 am
by mcog_esteban
feyd i have two questions:

i was testing this and i found that if i close the broswer, the session was not deleted from the database.
the session id is alwais the same, what is best time to generate a new id?
should i do it in the _read function when the query returns 0 rows?

Posted: Fri Apr 29, 2005 4:04 am
by onion2k
That's some very nice code there Feyd.