phpLens
 home  products examples manual  faq support forum  contact news  login store

Forums: phplens   adodb
Forums:   ADOdb Help & Dev:   Replies 
Search
Topic: Session management across servers
author: Charles Peterson   created: 09-08-2005 01:49:17 AM
I reworked adodb_session.php so it tracks ip addresses also.
When a user changes servers the session manager looks for the session id and ip address, if no match it looks for ip address and updates the new server to have the same session id so the user can go between the two, or more, servers that are using the same session database.
If you have any suggestions for improvements please email me.

DATABASE SCHEMA
<?xml version="1.0"?>
<schema version="0.2">
<table name="sessions">
<desc>table for session-management</desc>

<field name="SESSKEY" type="C" size="32">
<descr>session key</descr>
<KEY/>
<NOTNULL/>
</field>

<field name="EXPIRY" type="I" size="11">
<descr></descr>
<NOTNULL/>
</field>

<field name="EXPIREREF" type="C" size="64">
<descr></descr>
</field>

<field name="DATA_SESSION" type="XL">
<descr></descr>
<NOTNULL/>
</field>

<field name="IPADD" type="C" size="20">
<descr></descr>
</field>
</table>
</schema>


GRIP_SESSION.PHP

<?php
/* This code rewritten copyright 2005, distributed under the terms of the GNU Public License.
// written for PostgreSQL/MySQL Database by Charles A. Peterson
// Distributed under the GPL: http://www.gnu.org/copyleft/gpl.html
// restricted to licenses below for original code.
// switched ADODB_Session to GRIP_Session to not cause confusion
// added ipadd database column to track ipaddress,
// this class will update sessionkey when needed if ip matches.
// this allows to transfer session to other servers.
//-----------------------------------------------------------------*/

//********************************************************************************************

/*
V4.63 17 May 2005 (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.
Contributed by Ross Smith (adodb@netebb.com).
Released under both BSD license and Lesser GPL library license.
Whenever there is any discrepancy between the two licenses,
the BSD license will take precedence.
Set tabs to 4 for best viewing.
*/

require_once ( $CONFIG['adodb_dir'] . 'session/adodb-cryptsession.php' );

class GRIP_Session extends ADODB_Session {


function _init() {
session_module_name('user');
session_set_save_handler(
array('GRIP_Session', 'open'),
array('GRIP_Session', 'close'),
array('GRIP_Session', 'read'),
array('GRIP_Session', 'write'),
array('GRIP_Session', 'destroy'),
array('GRIP_Session', 'gc')
);
}

/*
Slurp in the session variables and return the serialized string
*/
function read($key) {
global $_SERVER;

$conn =& GRIP_Session::_conn();
$data = GRIP_Session::dataFieldName();
$filter = GRIP_Session::filter();
$table = GRIP_Session::table();

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

assert('$table');

$qkey = $conn->quote($key);
$binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : '';

$sql = "SELECT $data FROM $table WHERE $binary sesskey = $qkey AND expiry >= " . time() . " AND ipadd = '" . $_SERVER['REMOTE_ADDR'] . "'";

$rs =& $conn->Execute($sql);
//GRIP_Session::_dumprs($rs);
if ($rs && $rs->RecordCount()>0) {
if ($rs->EOF) {
$v = '';
} else {
$v = reset($rs->fields);
$filter = array_reverse($filter);
foreach ($filter as $f) {
if (is_object($f)) {
$v = $f->read($v, GRIP_Session::_sessionKey());
}
}
$v = rawurldecode($v);
}

$rs->Close();

GRIP_Session::_crc(strlen($v) . crc32($v));
return $v;
}
/* Modified by Charles A. Peterson
if we didn't find the session with the ip of the user
check to see if session id changed (different server)
if so, try to find change this session id to match other server */
else if($rs =& $conn->Execute("SELECT sesskey FROM $table WHERE expiry >= " . time() . " AND ipadd = '" . $_SERVER['REMOTE_ADDR'] . "'") && $rs->RecordCount()>0){
$v = reset($rs->fields);
session_id ( $v );
return GRIP_Session::read(session_id ());
}

return '';
}

/*!
*/
function driver($driver = null) {
static $_driver = 'mysql';
static $set = false;

if (!is_null($driver)) {
$_driver = trim($driver);
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_DRIVER'])) {
return $GLOBALS['ADODB_SESSION_DRIVER'];
}
}

return $_driver;
}

/*!
*/
function host($host = null) {
static $_host = 'localhost';
static $set = false;

if (!is_null($host)) {
$_host = trim($host);
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_CONNECT'])) {
return $GLOBALS['ADODB_SESSION_CONNECT'];
}
}

return $_host;
}

/*!
*/
function user($user = null) {
static $_user = 'root';
static $set = false;

if (!is_null($user)) {
$_user = trim($user);
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_USER'])) {
return $GLOBALS['ADODB_SESSION_USER'];
}
}

return $_user;
}

/*!
*/
function password($password = null) {
static $_password = '';
static $set = false;

if (!is_null($password)) {
$_password = $password;
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_PWD'])) {
return $GLOBALS['ADODB_SESSION_PWD'];
}
}

return $_password;
}

/*!
*/
function database($database = null) {
static $_database = 'xphplens_2';
static $set = false;

if (!is_null($database)) {
$_database = trim($database);
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_DB'])) {
return $GLOBALS['ADODB_SESSION_DB'];
}
}

return $_database;
}

/*!
*/
function persist($persist = null)
{
static $_persist = true;

if (!is_null($persist)) {
$_persist = trim($persist);
}

return $_persist;
}

/*!
*/
function lifetime($lifetime = null) {
static $_lifetime;
static $set = false;

if (!is_null($lifetime)) {
$_lifetime = (int) $lifetime;
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESS_LIFE'])) {
return $GLOBALS['ADODB_SESS_LIFE'];
}
}
if (!$_lifetime) {
$_lifetime = ini_get('session.gc_maxlifetime');
if ($_lifetime <= 1) {
// bug in PHP 4.0.3 pl 1 -- how about other versions?
//print "<h3>Session Error: PHP.INI setting <i>session.gc_maxlifetime</i>not set: $lifetime</h3>";
$_lifetime = 1440;
}
}

return $_lifetime;
}

/*!
*/
function debug($debug = null) {
static $_debug = false;
static $set = false;

if (!is_null($debug)) {
$_debug = (bool) $debug;

$conn = GRIP_Session::_conn();
if ($conn) {
$conn->debug = $_debug;
}
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESS_DEBUG'])) {
return $GLOBALS['ADODB_SESS_DEBUG'];
}
}

return $_debug;
}

/*!
*/
function expireNotify($expire_notify = null) {
static $_expire_notify;
static $set = false;

if (!is_null($expire_notify)) {
$_expire_notify = $expire_notify;
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_EXPIRE_NOTIFY'])) {
return $GLOBALS['ADODB_SESSION_EXPIRE_NOTIFY'];
}
}

return $_expire_notify;
}

/*!
*/
function table($table = null) {
static $_table = 'sessions';
static $set = false;

if (!is_null($table)) {
$_table = trim($table);
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_TBL'])) {
return $GLOBALS['ADODB_SESSION_TBL'];
}
}

return $_table;
}

/*!
*/
function optimize($optimize = null) {
static $_optimize = false;
static $set = false;

if (!is_null($optimize)) {
$_optimize = (bool) $optimize;
$set = true;
} elseif (!$set) {
// backwards compatibility
if (defined('ADODB_SESSION_OPTIMIZE')) {
return true;
}
}

return $_optimize;
}

/*!
*/
function syncSeconds($sync_seconds = null) {
static $_sync_seconds = 60;
static $set = false;

if (!is_null($sync_seconds)) {
$_sync_seconds = (int) $sync_seconds;
$set = true;
} elseif (!$set) {
// backwards compatibility
if (defined('ADODB_SESSION_SYNCH_SECS')) {
return ADODB_SESSION_SYNCH_SECS;
}
}

return $_sync_seconds;
}

/*!
*/
function clob($clob = null) {
static $_clob = false;
static $set = false;

if (!is_null($clob)) {
$_clob = strtolower(trim($clob));
$set = true;
} elseif (!$set) {
// backwards compatibility
if (isset($GLOBALS['ADODB_SESSION_USE_LOBS'])) {
return $GLOBALS['ADODB_SESSION_USE_LOBS'];
}
}

return $_clob;
}

/*!
*/
function dataFieldName($data_field_name = null) {
static $_data_field_name = 'data';

if (!is_null($data_field_name)) {
$_data_field_name = trim($data_field_name);
}

return $_data_field_name;
}

/*!
*/
function filter($filter = null) {
static $_filter = array();

if (!is_null($filter)) {
if (!is_array($filter)) {
$filter = array($filter);
}
$_filter = $filter;
}

return $_filter;
}

/*!
*/
function encryptionKey($encryption_key = null) {
static $_encryption_key = 'CRYPTED ADODB SESSIONS ROCK!';

if (!is_null($encryption_key)) {
$_encryption_key = $encryption_key;
}

return $_encryption_key;
}

/////////////////////
// private methods
/////////////////////

/*!
*/
function &_conn($conn=null) {
return $GLOBALS['ADODB_SESS_CONN'];
}

/*!
*/
function _crc($crc = null) {
static $_crc = false;

if (!is_null($crc)) {
$_crc = $crc;
}

return $_crc;
}

/*!
*/
function _sessionKey() {
// use this function to create the encryption key for crypted sessions
// crypt the used key, GRIP_Session::encryptionKey() as key and session_id() as salt
return crypt(GRIP_Session::encryptionKey(), session_id());
}

/*!
*/
function _dumprs($rs) {
$conn =& GRIP_Session::_conn();
$debug = GRIP_Session::debug();

if (!$conn) {
return;
}

if (!$debug) {
return;
}

if (!$rs) {
echo "<br />\$rs is null or false<br />\n";
return;
}

//echo "<br />\nAffected_Rows=",$conn->Affected_Rows(),"<br />\n";

if (!is_object($rs)) {
return;
}

require_once ADODB_SESSION.'/../tohtml.inc.php';
rs2html($rs);
}

/////////////////////
// public methods
/////////////////////

/*!
Create the connection to the database.

If $conn already exists, reuse that connection
*/
function open($save_path, $session_name, $persist = null) {
$conn =& GRIP_Session::_conn();

if ($conn) {
return true;
}

$database = GRIP_Session::database();
$debug = GRIP_Session::debug();
$driver = GRIP_Session::driver();
$host = GRIP_Session::host();
$password = GRIP_Session::password();
$user = GRIP_Session::user();

if (!is_null($persist)) {
GRIP_Session::persist($persist);
} else {
$persist = GRIP_Session::persist();
}

# these can all be defaulted to in php.ini
# assert('$database');
# assert('$driver');
# assert('$host');

// cannot use =& below - do not know why...
$conn =& ADONewConnection($driver);

if ($debug) {
$conn->debug = true;
// ADOConnection::outp( " driver=$driver user=$user pwd=$password db=$database ");
}

if ($persist) {
switch($persist) {
default:
case 'P': $ok = $conn->PConnect($host, $user, $password, $database); break;
case 'C': $ok = $conn->Connect($host, $user, $password, $database); break;
case 'N': $ok = $conn->NConnect($host, $user, $password, $database); break;
}
} else {
$ok = $conn->Connect($host, $user, $password, $database);
}

if ($ok) $GLOBALS['ADODB_SESS_CONN'] =& $conn;
else
ADOConnection::outp('<p>Session: connection failed</p>', false);


return $ok;
}

/*!
Close the connection
*/
function close() {
/*
$conn =& GRIP_Session::_conn();
if ($conn) $conn->Close();
*/
return true;
}

/*!
Write the serialized data to a database.

If the data has not been modified since the last read(), we do not write.
*/
function write($key, $val) {
$clob = GRIP_Session::clob();
$conn =& GRIP_Session::_conn();
$crc = GRIP_Session::_crc();
$data = GRIP_Session::dataFieldName();
$debug = GRIP_Session::debug();
$driver = GRIP_Session::driver();
$expire_notify = GRIP_Session::expireNotify();
$filter = GRIP_Session::filter();
$lifetime = GRIP_Session::lifetime();
$table = GRIP_Session::table();

if (!$conn) {
return false;
}
$qkey = $conn->qstr($key);

assert('$table');

$expiry = time() + $lifetime;

$binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : '';

// crc32 optimization since adodb 2.1
// now we only update expiry date, thx to sebastian thom in adodb 2.32
if ($crc !== false && $crc == (strlen($val) . crc32($val))) {
if ($debug) {
echo '<p>Session: Only updating date - crc32 not changed</p>';
}
$sql = "UPDATE $table SET expiry = ".$conn->Param('0')." WHERE $binary sesskey = ".$conn->Param('1')." AND expiry >= ".$conn->Param('2') ." AND ipadd = " . $conn->Param('3') . "";
$rs =& $conn->Execute($sql,array($expiry,$key,time(),$_SERVER['REMOTE_ADDR']));
GRIP_Session::_dumprs($rs);
if ($rs) {
$rs->Close();
}
return true;
}
$val = rawurlencode($val);
foreach ($filter as $f) {
if (is_object($f)) {
$val = $f->write($val, GRIP_Session::_sessionKey());
}
}

$arr = array('sesskey' => $key, 'expiry' => $expiry, $data => $val, 'expireref' => '');
$arr['ipadd'] = $_SERVER['REMOTE_ADDR'];

if ($expire_notify) {
$var = reset($expire_notify);
global $$var;
if (isset($$var)) {
$arr['expireref'] = $$var;
}
}

if (!$clob) { // no lobs, simply use replace()
$arr[$data] = $conn->qstr($val);
$rs = $conn->Replace($table, $arr, 'sesskey', $autoQuote = true);
GRIP_Session::_dumprs($rs);
} else {
// what value shall we insert/update for lob row?
switch ($driver) {
// empty_clob or empty_lob for oracle dbs
case 'oracle':
case 'oci8':
case 'oci8po':
case 'oci805':
$lob_value = sprintf('empty_%s()', strtolower($clob));
break;

// null for all other
default:
$lob_value = 'null';
break;
}

// do we insert or update? => as for sesskey
$rs =& $conn->Execute("SELECT COUNT(*) AS cnt FROM $table WHERE $binary sesskey = $qkey and ipadd = '" . $_SERVER['REMOTE_ADDR'] . "'");
GRIP_Session::_dumprs($rs);
if ($rs && reset($rs->fields) > 0) {
$sql = "UPDATE $table SET expiry = $expiry, $data = $lob_value, ipadd = '" . $_SERVER['REMOTE_ADDR'] . "' WHERE sesskey = $qkey";
} else {
$sql = "INSERT INTO $table (expiry, $data, sesskey, ipadd) VALUES ($expiry, $lob_value, $qkey, '" . $_SERVER['REMOTE_ADDR'] . "')";
}
if ($rs) {
$rs->Close();
}

$err = '';
$rs1 =& $conn->Execute($sql);
GRIP_Session::_dumprs($rs1);
if (!$rs1) {
$err = $conn->ErrorMsg()."\n";
}
$rs2 =& $conn->UpdateBlob($table, $data, $val, " sesskey=$qkey", strtoupper($clob));
GRIP_Session::_dumprs($rs2);
if (!$rs2) {
$err .= $conn->ErrorMsg()."\n";
}
$rs = ($rs && $rs2) ? true : false;
if ($rs1) {
$rs1->Close();
}
if (is_object($rs2)) {
$rs2->Close();
}
}

if (!$rs) {
ADOConnection::outp('<p>Session Replace: ' . $conn->ErrorMsg() . '</p>', false);
return false;
} else {
// bug in access driver (could be odbc?) means that info is not committed
// properly unless select statement executed in Win2000
if ($conn->databaseType == 'access') {
$sql = "SELECT sesskey FROM $table WHERE $binary sesskey = $qkey and ipadd = '" . $_SERVER['REMOTE_ADDR'] . "'";
$rs =& $conn->Execute($sql);
GRIP_Session::_dumprs($rs);
if ($rs) {
$rs->Close();
}
}
}/*
if (GRIP_Session::Lock()) {
$conn->CommitTrans();
}*/
return $rs ? true : false;
}

/*!
*/
function destroy($key) {
$conn =& GRIP_Session::_conn();
$table = GRIP_Session::table();
$expire_notify = GRIP_Session::expireNotify();

if (!$conn) {
return false;
}

assert('$table');

$qkey = $conn->quote($key);
$binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : '';

if ($expire_notify) {
reset($expire_notify);
$fn = next($expire_notify);
$savem = $conn->SetFetchMode(ADODB_FETCH_NUM);
$sql = "SELECT expireref, sesskey FROM $table WHERE $binary sesskey = $qkey";
$rs =& $conn->Execute($sql);
GRIP_Session::_dumprs($rs);
$conn->SetFetchMode($savem);
if (!$rs) {
return false;
}
if (!$rs->EOF) {
$ref = $rs->fields[0];
$key = $rs->fields[1];
//assert('$ref');
//assert('$key');
$fn($ref, $key);
}
$rs->Close();
}

$sql = "DELETE FROM $table WHERE $binary sesskey = $qkey";
$rs =& $conn->Execute($sql);
GRIP_Session::_dumprs($rs);
if ($rs) {
$rs->Close();
}

return $rs ? true : false;
}

/*!
*/
function gc($maxlifetime) {
$conn =& GRIP_Session::_conn();
$debug = GRIP_Session::debug();
$expire_notify = GRIP_Session::expireNotify();
$optimize = GRIP_Session::optimize();
$sync_seconds = GRIP_Session::syncSeconds();
$table = GRIP_Session::table();

if (!$conn) {
return false;
}

assert('$table');

$time = time();

$binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : '';

if ($expire_notify) {
reset($expire_notify);
$fn = next($expire_notify);
$savem = $conn->SetFetchMode(ADODB_FETCH_NUM);
$sql = "SELECT expireref, sesskey FROM $table WHERE expiry < $time";
$rs =& $conn->Execute($sql);
GRIP_Session::_dumprs($rs);
$conn->SetFetchMode($savem);
if ($rs) {
$conn->BeginTrans();
$keys = array();
while (!$rs->EOF) {
$ref = $rs->fields[0];
$key = $rs->fields[1];
$fn($ref, $key);
$del = $conn->Execute("DELETE FROM $table WHERE sesskey='$key'");
$rs->MoveNext();
}
$rs->Close();

$conn->CommitTrans();
}
} else {
$sql = "DELETE FROM $table WHERE expiry < $time";
$rs =& $conn->Execute($sql);
GRIP_Session::_dumprs($rs);
if ($rs) {
$rs->Close();
}
if ($debug) {
ADOConnection::outp("<p><b>Garbage Collection</b>: $sql</p>");
}
}

// suggested by Cameron, "GaM3R" <gamr@outworld.cx>
if ($optimize) {
$driver = GRIP_Session::driver();

if (preg_match('/mysql/i', $driver)) {
$sql = "OPTIMIZE TABLE $table";
}
if (preg_match('/postgres/i', $driver)) {
$sql = "VACUUM $table";
}
if (!empty($sql)) {
$conn->Execute($sql);
}
}

if ($sync_seconds) {
$sql = 'SELECT ';
if ($conn->dataProvider === 'oci8') {
$sql .= "TO_CHAR({$conn->sysTimeStamp}, 'RRRR-MM-DD HH24:MI:SS')";
} else {
$sql .= $conn->sysTimeStamp;
}
$sql .= " FROM $table";

$rs =& $conn->SelectLimit($sql, 1);
if ($rs && !$rs->EOF) {
$dbts = reset($rs->fields);
$rs->Close();
$dbt = $conn->UnixTimeStamp($dbts);
$t = time();

if (abs($dbt - $t) >= $sync_seconds) {
$msg = __FILE__ .
": Server time for webserver {$_SERVER['HTTP_HOST']} not in synch with database: " .
" database=$dbt ($dbts), webserver=$t (diff=". (abs($dbt - $t) / 60) . ' minutes)';
error_log($msg);
if ($debug) {
ADOConnection::outp("<p>$msg</p>");
}
}
}
}

return true;
}

}
GRIP_Session::dataFieldName('data_session');
GRIP_Session::filter(new ADODB_Encrypt_MD5());
GRIP_Session::_init();

?>
Topic: Re:Session management across servers
author: John Lim   created: 09-08-2005 12:29:42 PM
>> session id and ip address

On AOL and other systems with large proxy servers, the ip address is not reliable. Using a cookie with a sufficiently long unique session id is sufficient guarantee between different servers.
Topic: Re:Session management across servers
author: steve   created: 01-09-2010 04:42:28 PM
how can make this session class to workable in php version 5
Page 1
Search

View Source

email: contact#phplens.com (change # to @)     telephone (malaysia): 60-3-7947 2888     fax (malaysia): 60-3-7947 2800