Skip to content

Instantly share code, notes, and snippets.

@IngmarBoddington
Created October 25, 2012 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IngmarBoddington/3952638 to your computer and use it in GitHub Desktop.
Save IngmarBoddington/3952638 to your computer and use it in GitHub Desktop.
PHP Database Session Handler
<?php
/**
* SessionHandler Class - Uses DB for session handling (for multiserver environment)
*
* @package glowingminds
* @author Ingmar Boddington
*/
class SessionHandler {
const TABLENAME = 'sessions';
private $_databaseConnection;
/**
* Setup with PDO
*
* @param PDO $_databaseConnection
*/
public function __construct(PDO $_databaseConnection) {
$this->_databaseConnection = $_databaseConnection;
}
/**
* Ready Database Table
*
* @param string $savePath
* @param int $sessionId
* @throws Exception
* @return boolean
*/
public function open($savePath, $sessionId) {
//Create table if it does not exist
try {
$sql = '
CREATE TABLE IF NOT EXISTS '.self::TABLENAME.'
(
sessionId VARCHAR(100) PRIMARY KEY,
sessionLastSave INT NULL,
sessionData TEXT NULL
);
';
$statement = $this->_databaseConnection->prepare($sql);
if (!$statement) {
throw new Exception('Failed to prepare statement for generation of ' . self::TABLENAME . ', errorInfo = ' . implode('|', $this->_databaseConnection->errorInfo()));
}
$success = $statement->execute();
if (!$success) {
throw new Exception('Failed to execute sql for generation of ' . self::TABLENAME . ', errorInfo = ' . implode('|', $statement->errorInfo()) . ', debugDumpParams = ' . implode('|', $statement->debugDumpParams()));
}
$statement = NULL;
} catch (Exception $e) {
//rethrow the exception for control code
throw $e;
}
return true;
}
/**
* Cleanup
*
* @return boolean
*/
public function close() {
$this->_databaseConnection = NULL;
return true;
}
/**
* Read Session Data
*
* @param int $sessionId
* @throws Exception
* @return String
*/
public function read($sessionId) {
//Return matching session data
try {
$sql = '
SELECT sessionData
FROM ' . self::TABLENAME . '
WHERE sessionId = ?;
';
$statement = $this->_databaseConnection->prepare($sql);
if (!$statement) {
throw new Exception('Failed to prepare statement for session data fetch in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $this->_databaseConnection->errorInfo()));
}
$success = $statement->execute(array($sessionId));
if (!$success) {
throw new Exception('Failed to execute sql for session data fetch in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $statement->errorInfo()) . ', debugDumpParams = ' . implode('|', $statement->debugDumpParams()));
}
/* This will cause failure, as session handler appears to attempt read before 1st write
if ($statement->rowCount() != 1) {
throw new Exception('Failed to fetch 1 row (found ' . $statement->rowCount() . ') for session data fetch in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $statement->errorInfo()));
}
*/
list($sessionData) = $statement->fetch(PDO::FETCH_NUM);
$statement = NULL;
} catch (Exception $e) {
//rethrow the exception for control code
throw $e;
}
return $sessionData;
}
/**
* Write Session Data
*
* @param int $sessionId
* @param string $sessionData
* @throws Exception
* @return boolean
*/
public function write($sessionId, $sessionData) {
try {
$sessionLastSave = time();
//Update record
$sql = '
REPLACE INTO ' . self::TABLENAME . '
(sessionId, sessionLastSave, sessionData)
VALUES
(?, ?, ?);
';
$statement = $this->_databaseConnection->prepare($sql);
if (!$statement) {
throw new Exception('Failed to prepare statement for session data update in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $this->_databaseConnection->errorInfo()));
}
$success = $statement->execute(array($sessionId, $sessionLastSave, $sessionData));
if (!$success) {
throw new Exception('Failed to execute sql for session data update in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $statement->errorInfo()) . ', debugDumpParams = ' . implode('|', $statement->debugDumpParams()));
}
$statement = NULL;
} catch (Exception $e) {
//rethrow the exception for control code
throw $e;
}
return true;
}
/**
* Destroy A Session
*
* @param int $sessionId
* @throws Exception
* @return boolean
*/
public function destroy($sessionId) {
//Delete record
try {
$sql = '
DELETE FROM ' . self::TABLENAME . '
WHERE sessionId = ?
';
$statement = $this->_databaseConnection->prepare($sql);
if (!$statement) {
throw new Exception('Failed to prepare statement for session data removal in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $this->_databaseConnection->errorInfo()));
}
$success = $statement->execute(array($sessionId));
if (!$success) {
throw new Exception('Failed to execute sql for session data removal in table ' . self::TABLENAME . ' where sessionId = ' . $sessionId . ', errorInfo = ' . implode('|', $statement->errorInfo()) . ', debugDumpParams = ' . implode('|', $statement->debugDumpParams()));
}
$statement = NULL;
} catch (Exception $e) {
//rethrow the exception for control code
throw $e;
}
return true;
}
/**
* Garbage Collection
*
* @param string $maxLifetime
* @throws Exception
* @return boolean
*/
public function gc($maxLifetime) {
//Delete old records
try {
$checkStamp = time() - $maxLifetime;
$sql = '
DELETE FROM ' . self::TABLENAME . '
WHERE sessionLastSave < ?
OR sessionLastSave IS NULL;
';
$statement = $this->_databaseConnection->prepare($sql);
if (!$statement) {
throw new Exception('Failed to prepare statement for session data cleanup in table ' . self::TABLENAME . ' where checkStamp = ' . $checkStamp . ', errorInfo = ' . implode('|', $this->_databaseConnection->errorInfo()));
}
$success = $statement->execute(array($checkStamp));
if (!$success) {
throw new Exception('Failed to execute sql for session data cleanup in table ' . self::TABLENAME . ' where checkStamp = ' . $checkStamp . ', errorInfo = ' . implode('|', $statement->errorInfo()) . ', debugDumpParams = ' . implode('|', $statement->debugDumpParams()));
}
$statement = NULL;
} catch (Exception $e) {
//rethrow the exception for control code
throw $e;
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment