Forums | MacLife
You are not logged in.
#1 2009-07-05 2:00 am
db + apc session management
Most the credit goes to Rich Smith - he wrote the mysql session management class I based this off of.
I ported it to MDB2 and added APC caching of the session data.
Not super tested (the mbd2 port is, the apc addition I just did) but tested to my satisfaction over last 24 hours.
If you find it useful, enjoy.
Code:
<?php
//require_once("sessions_apc.php");
//$sess = new SessionManager($mdb2);
//session_start();
// from :
// http://www.devshed.com/c/a/PHP/Storing-PHP-Sessions-in-a-Database/
// Rich Smith - 2007-05-02
//
// Modified by mpeters@mac.com to use mdb2 w/ prepared statements
// and attempt to use caching
class SessionManager {
public $sesstable = 'new_sessions';
private $life_time;
private $mdb2;
// CHANGE THE SALT BEFORE USING
private $apcSalt = '2d8l009faQWveMKKLybafz';
private $apcMaxLife = 900; // delete from cache after that many seconds
// even if session still active
function SessionManager($mdb2) {
// constructor function
// Read the maxlifetime setting from PHP
$this->life_time = get_cfg_var("session.gc_maxlifetime");
$this->mdb2 = $mdb2;
// 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 = '';
$myreturn = $this->wrap_fetch($id);
if (! $myreturn) {
// Fetch session data from the selected database
$time = time();
$types = Array('text','integer');
$q = 'SELECT session_data FROM ' . $this->sesstable . ' WHERE session_id=? AND expires > ?';
$sql = $this->mdb2->prepare($q,$types,MDB2_PREPARE_RESULT);
// if(PEAR::isError($sql)) {
// die('Failed to make prepared 58: ' . $sql->getMessage() . ', ' . $sql->getDebugInfo());
// }
$args = Array($id,$time);
$rs = $sql->execute($args);
// if(PEAR::isError($rs)) {
// die('Failed to issue query 63: ' . $rs->getMessage() . ', ' . $rs->getDebugInfo());
// }
if ($rs->numRows() > 0) {
$row = $rs->fetchRow(MDB2_FETCHMODE_OBJECT);
$myreturn = $row->session_data;
} else {
$myreturn = '';
}
}
return $myreturn;
}
function write($id,$data) {
// Build query
$time = time() + $this->life_time;
// see if a session exists
$sessTest = wrap_fetch($id);
if (! $sessTest) {
$types = Array('text');
$q = 'SELECT COUNT(session_id) from ' . $this->sesstable . ' WHERE session_id=?';
$sql = $this->mdb2->prepare($q,$types,MDB2_PREPARE_RESULT);
//if (PEAR::isError($sql)) {
// die('Failed to make prepared 86: ' . $sql->getMessage() . ', ' . $sql->getDebugInfo());
// }
$args = Array($id);
$rs = $sql->execute($args);
//if(PEAR::isError($rs)) {
// die('Failed to issue query 91: ' . $rs->getMessage() . ', ' . $rs->getDebugInfo());
// }
$row = $rs->fetchRow(MDB2_FETCHMODE_ORDERED);
$count = $row[0];
} else {
$count = 1;
}
if ($count > 0) {
// update the session
$types = Array('text','integer','text');
$q = 'UPDATE ' . $this->sesstable . ' SET session_data=?, expires=? WHERE session_id=?';
$args = Array($data,$time,$id);
} else {
$types = Array('text','text','integer');
$q = 'INSERT INTO ' . $this->sesstable . ' (session_id,session_data,expires) VALUES (?,?,?)';
$args = Array($id,$data,$time);
}
$sql = $this->mdb2->prepare($q,$types,MDB2_PREPARE_MANIP);
//if(PEAR::isError($sql)) {
// die('Failed to make prepared 111: ' . $sql->getMessage() . ', ' . $sql->getDebugInfo());
// }
$rs = $sql->execute($args);
//if(PEAR::isError($rs)) {
// die('Failed to issue query 115: ' . $rs->getMessage() . ', ' . $rs->getDebugInfo());
// }
$this->wrap_store($id,$data);
return TRUE;
}
function destroy($id) {
// Build query
$this->wrap_delete($id);
$types = Array('text');
$args = Array($id);
$q = 'DELETE FROM ' . $this->sesstable . ' WHERE session_id=?';
$sql = $this->mdb2->prepare($q,$types,MDB2_PREPARE_MANIP);
//if(PEAR::isError($sql)) {
// die('Failed to make prepared 129: ' . $sql->getMessage() . ', ' . $sql->getDebugInfo());
// }
$rs = $sql->execute($args);
//if(PEAR::isError($rs)) {
// die('Failed to issue query 133: ' . $rs->getMessage() . ', ' . $rs->getDebugInfo());
// }
return TRUE;
}
function gc() {
// Garbage Collection
// Build DELETE query. Delete all records who have passed the expiration time
$sql = 'DELETE FROM ' . $this->sesstable . ' WHERE expires < UNIX_TIMESTAMP();';
$rs = $this->mdb2->execute($sql);
// Always return TRUE
return true;
}
// APC functions
function obfus($id) {
// this reduces odds of session hijacking if
// a cracker manages to get a dump of apc keys
$key = 'sess_' . sha1($this->apcSalt . $id);
return $key;
}
function wrap_delete($id) {
$key = $this->obfus($id);
if (function_exists('apc_delete')) {
apc_delete($key);
}
return true;
}
function wrap_fetch($id) {
$key = $this->obfus($id);
if (function_exists('apc_fetch')) {
$data = apc_fetch($key);
return $data;
} else {
return false;
}
}
function wrap_store($id,$data) {
$key = $this->obfus($id);
$expires = $this->life_time;
if ($expires < 60) {
// keep it in cache for 1 minute
$expires = 60;
} elseif ($expires > $this->apcMaxLife) {
// keep it in cache for
$expires = $this->apcMaxLife;
}
if (function_exists('apc_store')) {
apc_store($key,$data,$expires);
}
return true;
}
}
// CREATE TABLE new_sessions (
// session_id varchar(32) NOT NULL default '',
// session_data text,
// expires int(11) NOT NULL default '0',
// PRIMARY KEY (session_id)
// ) ENGINE = MYISAM;
?>In her right hand Jenny held the Bible of her mother
Jenny had a pistol in the other
-- Steve Taylor
Offline
#2 2009-07-05 8:14 am
Re: db + apc session management
I've since run into a few scenarios where I can not log in to my page until I delete the cookie - I don't know if it is this new session caching or if it is other changes I made, but I'll figure it out.
Anyway, since I posted the script, thought I'd post a problem that may be related to it's use.
EDIT -
The problem was an edit I had made to my login page, not the script.
Last edited by resedit (2009-07-05 5:06 pm)
In her right hand Jenny held the Bible of her mother
Jenny had a pistol in the other
-- Steve Taylor
Offline
