2013-01-30 21:51:28 -05:00

364 lines
11 KiB
PHP

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Database container for session data
*
* PEAR::MDB2 database container
*
* PHP version 4
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category HTTP
* @package HTTP_Session
* @author Alexander Radivanovich <info@wwwlab.net>
* @author David Costa <gurugeek@php.net>
* @author Michael Metz <pear.metz@speedpartner.de>
* @author Stefan Neufeind <pear.neufeind@speedpartner.de>
* @author Torsten Roehr <torsten.roehr@gmx.de>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: MDB2.php,v 1.5 2007/07/14 12:11:55 troehr Exp $
* @link http://pear.php.net/package/HTTP_Session
* @since File available since Release 0.5.0
*/
require_once 'HTTP/Session/Container.php';
require_once 'MDB2.php';
/**
* Database container for session data
*
* Create the following table to store session data
* <code>
* CREATE TABLE `sessiondata` (
* `id` CHAR(32) NOT NULL,
* `expiry` INT UNSIGNED NOT NULL DEFAULT 0,
* `data` TEXT NOT NULL,
* PRIMARY KEY (`id`)
* );
* </code>
*
* @category HTTP
* @package HTTP_Session
* @author David Costa <gurugeek@php.net>
* @author Michael Metz <pear.metz@speedpartner.de>
* @author Stefan Neufeind <pear.neufeind@speedpartner.de>
* @author Torsten Roehr <torsten.roehr@gmx.de>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: @package_version@
* @link http://pear.php.net/package/HTTP_Session
* @since Class available since Release 0.5.0
*/
class HTTP_Session_Container_MDB2 extends HTTP_Session_Container
{
/**
* MDB2 connection object
*
* @var object MDB2
* @access private
*/
var $db = null;
/**
* Session data cache id
*
* @var mixed
* @access private
*/
var $crc = false;
/**
* Constructor method
*
* $options is an array with the options.<br>
* The options are:
* <ul>
* <li>'dsn' - The DSN string</li>
* <li>'table' - Table with session data, default is 'sessiondata'</li>
* <li>'autooptimize' - Boolean, 'true' to optimize
* the table on garbage collection, default is 'false'.</li>
* </ul>
*
* @param array $options Options
*
* @access public
* @return void
*/
function HTTP_Session_Container_MDB2($options)
{
$this->_setDefaults();
if (is_array($options)) {
$this->_parseOptions($options);
} else {
$this->options['dsn'] = $options;
}
}
/**
* Connect to database by using the given DSN string
*
* @param string $dsn DSN string
*
* @access private
* @return mixed Object on error, otherwise bool
*/
function _connect($dsn)
{
if (is_string($dsn) || is_array($dsn)) {
$this->db = MDB2::connect($dsn);
} else if (is_object($dsn) && is_a($dsn, 'MDB2_Driver_Common')) {
$this->db = $dsn;
} else if (is_object($dsn) && MDB2::isError($dsn)) {
return $dsn;
} else {
return new PEAR_Error("The given dsn was not valid in file " . __FILE__
. " at line " . __LINE__,
41,
PEAR_ERROR_RETURN,
null,
null
);
}
if (MDB2::isError($this->db)) {
return new MDB2_Error($this->db->code, PEAR_ERROR_DIE);
}
return true;
}
/**
* Set some default options
*
* @access private
* @return void
*/
function _setDefaults()
{
$this->options['dsn'] = null;
$this->options['table'] = 'sessiondata';
$this->options['autooptimize'] = false;
}
/**
* Establish connection to a database
*
* @param string $save_path Save path
* @param string $session_name Session name
*
* @return bool
*/
function open($save_path, $session_name)
{
if (MDB2::isError($this->_connect($this->options['dsn']))) {
return false;
} else {
return true;
}
}
/**
* Free resources
*
* @return bool
*/
function close()
{
return true;
}
/**
* Read session data
*
* @param string $id Session id
*
* @return mixed
*/
function read($id)
{
$query = sprintf("SELECT data FROM %s WHERE id = %s AND expiry >= %d",
$this->options['table'],
$this->db->quote(md5($id), 'text'),
time());
$result = $this->db->queryOne($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
$this->crc = strlen($result) . crc32($result);
return $result;
}
/**
* Write session data
*
* @param string $id Session id
* @param mixed $data Data
*
* @return bool
*/
function write($id, $data)
{
if ((false !== $this->crc) &&
($this->crc === strlen($data) . crc32($data))) {
// $_SESSION hasn't been touched, no need to update the blob column
$query = sprintf("UPDATE %s SET expiry = %d WHERE id = %s",
$this->options['table'],
time() + ini_get('session.gc_maxlifetime'),
$this->db->quote(md5($id), 'text'));
} else {
// Check if table row already exists
$query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s",
$this->options['table'],
$this->db->quote(md5($id), 'text'));
$result = $this->db->queryOne($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
if (0 == intval($result)) {
// Insert new row into table
$query = sprintf("INSERT INTO %s (id, expiry, data) VALUES (%s, %d, %s)",
$this->options['table'],
$this->db->quote(md5($id), 'text'),
time() + ini_get('session.gc_maxlifetime'),
$this->db->quote($data, 'text'));
} else {
// Update existing row
$query = sprintf("UPDATE %s SET expiry = %d, data = %s WHERE id = %s",
$this->options['table'],
time() + ini_get('session.gc_maxlifetime'),
$this->db->quote($data, 'text'),
$this->db->quote(md5($id), 'text'));
}
}
$result = $this->db->query($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
return true;
}
/**
* Destroy session data
*
* @param string $id Session id
*
* @return bool
*/
function destroy($id)
{
$query = sprintf("DELETE FROM %s WHERE id = %s",
$this->options['table'],
$this->db->quote(md5($id), 'text'));
$result = $this->db->query($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
return true;
}
/**
* Replicate session data to table specified in option 'replicateBeforeDestroy'
*
* @param string $targetTable Table to replicate to
* @param string $id Id of record to replicate
*
* @access private
* @return bool
*/
function replicate($targetTable, $id = null)
{
if (is_null($id)) {
$id = HTTP_Session::id();
}
// Check if table row already exists
$query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s",
$targetTable,
$this->db->quote(md5($id), 'text'));
$result = $this->db->queryOne($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
// Insert new row into dest table
if (0 == intval($result)) {
$query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = %s",
$targetTable,
$this->options['table'],
$this->db->quote(md5($id), 'text'));
} else {
// Update existing row
$query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id AND src.id = %s",
$targetTable,
$this->options['table'],
$this->db->quote(md5($id), 'text'));
}
$result = $this->db->query($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
return true;
}
/**
* Garbage collection
*
* @param int $maxlifetime Maximum lifetime
*
* @return bool
*/
function gc($maxlifetime)
{
$query = sprintf("DELETE FROM %s WHERE expiry < %d",
$this->options['table'],
time());
$result = $this->db->query($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
if ($this->options['autooptimize']) {
switch($this->db->phptype) {
case 'mysql':
$query = sprintf("OPTIMIZE TABLE %s", $this->options['table']);
break;
case 'pgsql':
$query = sprintf("VACUUM %s", $this->options['table']);
break;
default:
$query = null;
break;
}
if (isset($query)) {
$result = $this->db->query($query);
if (MDB2::isError($result)) {
$this->db->raiseError($result->code, PEAR_ERROR_DIE);
return false;
}
}
}
return true;
}
}
?>