<?php
/**
* PHPStopFlood_Container_MDB2 class definition
* @package Containers
*
*/
require_once dirname(__FILE__) . '/../Container.php';
/**
* PEAR::MDB2 container class. This class stores PHPStopFlood data in database using
* PEAR::MDB2 class to access this data.
*
* To use this container you need to have {@link http://pear.php.net/package/MDB2 PEAR::MDB2 package}
* installed
*
* Container supports the following options:
* - <b>db_object</b> - Required. PEAR::MDB2 class instance to access database
* - <b>table</b> - Required. String. Table name where to store data
* Create the following table to store counter logs:
* <pre>
* CREATE TABLE `fc_logs` (
* //Replace 32 below with the maximum length of UniqueId you will use
* `unique_id` varchar(32) NOT NULL
* `data` text NOT NULL,
* `access` int UNSIGNED NOT NULL,
* PRIMARY KEY (`unique_id`)
* );
* </pre>
* - <b>autooptimize</b> - Optional. Boolean. Defaults to False. If True - database table
* optimization will be executed at the end of each garbage collection cycle. This operation
* improves read/write performance but takes much time to execute so please ensure you have issued
* {@link PHPStopFlood::setProbability} with small enough value like 0.1 for example before using this
* option. Otherwise hackers may DoS your database server.
*
*
* @package PHPStopFlood
* @subpackage Containers
* @author Vladislav Rastrusny <hide@address.com>
* @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
* @link http://pear.php.net/package/PHPStopFlood
*/
class PHPStopFlood_Container_PEAR_MDB2 extends PHPStopFlood_Container {
/**
* Database object instance
*
* @internal
* @var object
*/
protected $_db = null;
/**
* @internal
* @see PHPStopFlood_Container::_setDefaults()
*/
protected function _setDefaults() {
$this->_options ['db_object'] = '';
$this->_options ['table'] = 'fc_logs';
$this->_options ['autooptimize'] = false;
}
/**
* @internal
* @see PHPStopFlood_Container::set()
*/
protected function _initialize() {
if (! ($this->_options ['db_object'] instanceof MDB2_Driver_Common)) {
throw new PHPStopFlood_Exception('PEAR::MDB2 database object is not set!');
}
$this->_db = $this->_options ['db_object'];
if (PEAR::isError($this->_db)) {
throw new PHPStopFlood_Exception($this->_db->getMessage(), $this->_db->getCode());
}
}
/**
* @internal
* @see PHPStopFlood_Container::read()
*/
public function read($uniqueId) {
$query = sprintf("SELECT data FROM %s WHERE unique_id = %s",
$this->_db->quoteIdentifier($this->_options ['table']),
$this->_db->quote($uniqueId, 'text'));
$result = $this->_db->queryOne($query);
if (PEAR::isError($result)) {
throw new PHPStopFlood_Exception($result->getMessage(), $result->getCode());
}
if (is_null($result)) {
return array();
} else {
return @unserialize($result);
}
}
/**
* @internal
* @see PHPStopFlood_Container::write()
*/
public function write($uniqueId, array $data) {
$quotedTblName = $this->_db->quoteIdentifier($this->_options ['table']);
$this->_db->beginTransaction();
$query = sprintf("DELETE FROM %s WHERE unique_id = %s", $quotedTblName,
$this->_db->quote($uniqueId, 'text'));
$result = $this->_db->exec($query);
if (PEAR::isError($result)) {
throw new PHPStopFlood_Exception($result->getMessage(), $result->getCode());
}
$query = sprintf("INSERT INTO %s (unique_id, data, access) VALUES (%s, %s, %d)",
$quotedTblName, $this->_db->quote($uniqueId, 'text'),
$this->_db->quote(serialize($data), 'text'), time());
$result = $this->_db->exec($query);
if (PEAR::isError($result)) {
$this->_db->rollback();
throw new PHPStopFlood_Exception($result->getMessage(), $result->getCode());
}
$this->_db->commit();
}
/**
* @internal
* @see PHPStopFlood_Container::gc()
*/
public function gc($lifetime) {
$quotedTblName = $this->_db->quoteIdentifier($this->_options ['table']);
$query = sprintf("DELETE FROM %s WHERE access < %d", $quotedTblName, time() - $lifetime);
$result = $this->_db->exec($query);
if (PEAR::isError($result)) {
throw new PHPStopFlood_Exception($result->getMessage(), $result->getCode());
}
if ($this->_options ['autooptimize']) {
switch ( $this->_db->phptype) {
case 'mysql' :
$query = sprintf("OPTIMIZE TABLE %s", $quotedTblName);
break;
case 'pgsql' :
$query = sprintf("VACUUM %s", $quotedTblName);
break;
default :
$query = null;
break;
}
if (! is_null($query)) {
$result = $this->_db->exec($query);
if (PEAR::isError($result)) {
throw new PHPStopFlood_Exception($result->getMessage(), $result->getCode());
}
}
}
}
}
?>