* Copyright 2001-2009 Strangecode Internet Consultancy
*
* This file is part of The Strangecode Codebase.
*
* The Strangecode Codebase is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* The Strangecode Codebase is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* The Strangecode Codebase. If not, see .
*/
/**
* Prefs.inc.php
*
* Prefs provides an API for saving arbitrary values in a user's session.
* Session prefs can be stored into a database with the optional save() and load() methods.
*
* @author Quinn Comendant
* @version 2.1
*
* Example of use:
---------------------------------------------------------------------
// Load preferences for the user's session.
require_once 'codebase/lib/Prefs.inc.php';
$prefs = new Prefs('my-namespace');
$prefs->setParam(array(
'persistent' => $auth->isLoggedIn(),
'user_id' => $auth->get('user_id'),
));
$prefs->setDefaults(array(
'search_num_results' => 25,
'datalog_num_entries' => 25,
));
$prefs->load();
// Update preferences. Make sure to validate this input first!
$prefs->set('search_num_results', getFormData('search_num_results'));
$prefs->set('datalog_num_entries', getFormData('datalog_num_entries'));
$prefs->save();
---------------------------------------------------------------------
*/
class Prefs {
// Namespace of this instance of Prefs.
var $_ns;
// Configuration parameters for this object.
var $_params = array(
// Enable database storage. If this is false, all prefs will live only as long as the session.
'persistent' => false,
// The current user_id for which to load/save persistent preferences.
'user_id' => null,
// How long before we force a reload of the persistent prefs data? 3600 = once every hour.
'load_timeout' => 3600,
// Name of database table to store persistent prefs.
'db_table' => 'pref_tbl',
// Automatically create table and verify columns. Better set to false after site launch.
'create_table' => true,
);
/**
* Prefs constructor.
*/
function Prefs($namespace='')
{
$app =& App::getInstance();
$this->_ns = $namespace;
// Initialized the prefs array.
if (!isset($_SESSION['_prefs'][$this->_ns])) {
$this->clear();
}
// Get create tables config from global context.
if (!is_null($app->getParam('db_create_tables'))) {
$this->setParam(array('create_table' => $app->getParam('db_create_tables')));
}
}
/**
* Setup the database table for this class.
*
* @access public
* @author Quinn Comendant
* @since 04 Jun 2006 16:41:42
*/
function initDB($recreate_db=false)
{
$app =& App::getInstance();
$db =& DB::getInstance();
static $_db_tested = false;
if ($recreate_db || !$_db_tested && $this->getParam('create_table')) {
if ($recreate_db) {
$db->query("DROP TABLE IF EXISTS " . $this->getParam('db_table'));
$app->logMsg(sprintf('Dropping and recreating table %s.', $this->getParam('db_table')), LOG_INFO, __FILE__, __LINE__);
}
$db->query("CREATE TABLE IF NOT EXISTS " . $db->escapeString($this->getParam('db_table')) . " (
user_id VARCHAR(32) NOT NULL DEFAULT '',
pref_namespace VARCHAR(32) NOT NULL DEFAULT '',
pref_key VARCHAR(64) NOT NULL DEFAULT '',
pref_value TEXT,
PRIMARY KEY (user_id, pref_namespace, pref_key)
)");
if (!$db->columnExists($this->getParam('db_table'), array(
'user_id',
'pref_namespace',
'pref_key',
'pref_value',
), false, false)) {
$app->logMsg(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), LOG_ALERT, __FILE__, __LINE__);
trigger_error(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), E_USER_ERROR);
}
}
$_db_tested = true;
}
/**
* Set the params of this object.
*
* @param array $params Array of param keys and values to set.
*/
function setParam($params=null)
{
if (isset($params) && is_array($params)) {
// Merge new parameters with old overriding only those passed.
$this->_params = array_merge($this->_params, $params);
}
}
/**
* Return the value of a parameter, if it exists.
*
* @access public
* @param string $param Which parameter to return.
* @return mixed Configured parameter value.
*/
function getParam($param)
{
$app =& App::getInstance();
if (isset($this->_params[$param])) {
return $this->_params[$param];
} else {
$app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
return null;
}
}
/**
* Sets the default values for preferences. If a preference is not explicitly
* set, the value set here will be used. Can be called multiple times to merge additional
* defaults together.
*
* @param array $defaults Array of key-value pairs
*/
function setDefaults($defaults)
{
if (isset($defaults) && is_array($defaults)) {
$_SESSION['_prefs'][$this->_ns]['defaults'] = array_merge($_SESSION['_prefs'][$this->_ns]['defaults'], $defaults);
}
}
/**
* Store a key-value pair in the session. If the value is different than what is set by setDefaults
* the value will be scheduled to be saved in the database.
* This function determines what data is saved to the database. Ensure clean values!
*
* @param string $key The name of the preference to modify.
* @param string $val The new value for this preference.
* @param bool $persistent Save this value forever? Set to false and value will exist as long as the session is in use.
*/
function set($key, $val)
{
$app =& App::getInstance();
if ('' == $key) {
$app->logMsg(sprintf('Key is empty (provided with value: %s)', $val), LOG_NOTICE, __FILE__, __LINE__);
return false;
}
// Set a persistent preference if...
// - there isn't a default.
// - the new value is different than the default
// - there is a previously existing persistent key.
if (!isset($_SESSION['_prefs'][$this->_ns]['defaults'][$key]) || $_SESSION['_prefs'][$this->_ns]['defaults'][$key] != $val || isset($_SESSION['_prefs'][$this->_ns]['persistent'][$key])) {
$_SESSION['_prefs'][$this->_ns]['persistent'][$key] = $val;
$app->logMsg(sprintf('Setting preference %s => %s', $key, truncate(getDump($val, true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
} else {
$app->logMsg(sprintf('Not setting preference %s => %s', $key, truncate(getDump($val, true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
}
}
/**
* Returns the value of the requested preference. Persistent values take precedence, but if none is set
* a default value is returned, or if not that, null.
*
* @param string $key The name of the preference to retrieve.
*
* @return string The value of the preference.
*/
function get($key)
{
$app =& App::getInstance();
if (isset($_SESSION['_prefs'][$this->_ns]['persistent']) && array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['persistent'])) {
$app->logMsg(sprintf('Found %s in persistent', $key), LOG_DEBUG, __FILE__, __LINE__);
return $_SESSION['_prefs'][$this->_ns]['persistent'][$key];
} else if (isset($_SESSION['_prefs'][$this->_ns]['defaults']) && array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['defaults'])) {
$app->logMsg(sprintf('Found %s in defaults', $key), LOG_DEBUG, __FILE__, __LINE__);
return $_SESSION['_prefs'][$this->_ns]['defaults'][$key];
} else {
$app->logMsg(sprintf('Key not found in prefs cache: %s', $key), LOG_DEBUG, __FILE__, __LINE__);
return null;
}
}
/**
* To see if a preference has been set.
*
* @param string $key The name of the preference to check.
* @return boolean True if the preference isset and not empty false otherwise.
*/
function exists($key)
{
return array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['persistent']);
}
/**
* Clear a set preference value. This will also remove the value from the database.
*
* @param string $key The name of the preference to delete.
*/
function delete($key)
{
unset($_SESSION['_prefs'][$this->_ns]['persistent'][$key]);
}
/**
* Resets the $_SESSION cache. This should be executed with the same consideration
* as $auth->clear(), such as when logging out.
*/
function clear($focus='all')
{
switch ($focus) {
case 'all' :
$_SESSION['_prefs'][$this->_ns] = array(
'loaded' => false,
'load_datetime' => '1970-01-01',
'defaults' => array(),
'persistent' => array(),
);
break;
case 'defaults' :
$_SESSION['_prefs'][$this->_ns]['defaults'] = array();
break;
case 'persistent' :
$_SESSION['_prefs'][$this->_ns]['persistent'] = array();
break;
}
}
/*
* Retrieves all prefs from the database and stores them in the $_SESSION.
*
* @access public
* @param bool $force Set to always load from database, regardless if _isLoaded() or not.
* @return bool True if loading succeeded.
* @author Quinn Comendant
* @version 1.0
* @since 04 Jun 2006 16:56:53
*/
function load($force=false)
{
$app =& App::getInstance();
$db =& DB::getInstance();
// Skip this method if not using the db.
if (true !== $this->getParam('persistent')) {
return true;
}
$this->initDB();
// Prefs already loaded for this session.
if (!$force && $this->_isLoaded()) {
return true;
}
// User_id must not be empty.
if ('' == $this->getParam('user_id')) {
$app->logMsg(sprintf('Cannot save prefs because user_id not set.', null), LOG_WARNING, __FILE__, __LINE__);
return false;
}
// Clear existing cache.
$this->clear('persistent');
// Retrieve all prefs for this user and namespace.
$qid = $db->query("
SELECT pref_key, pref_value
FROM " . $db->escapeString($this->getParam('db_table')) . "
WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
LIMIT 10000
");
while (list($key, $val) = mysql_fetch_row($qid)) {
$_SESSION['_prefs'][$this->_ns]['persistent'][$key] = unserialize($val);
}
$app->logMsg(sprintf('Loaded %s prefs from database.', mysql_num_rows($qid)), LOG_DEBUG, __FILE__, __LINE__);
// Data loaded only once per session.
$_SESSION['_prefs'][$this->_ns]['loaded'] = true;
$_SESSION['_prefs'][$this->_ns]['load_datetime'] = date('Y-m-d H:i:s');
return true;
}
/*
* Returns true if the prefs had been loaded from the database into the $_SESSION recently.
* This function is simply a check so the database isn't access every page load.
*
* @access private
* @return bool True if prefs are loaded.
* @author Quinn Comendant
* @version 1.0
* @since 04 Jun 2006 17:12:44
*/
function _isLoaded()
{
if (isset($_SESSION['_prefs'][$this->_ns]['load_datetime'])
&& strtotime($_SESSION['_prefs'][$this->_ns]['load_datetime']) > time() - $this->getParam('load_timeout')
&& isset($_SESSION['_prefs'][$this->_ns]['loaded'])
&& true === $_SESSION['_prefs'][$this->_ns]['loaded']) {
return true;
} else {
return false;
}
}
/*
* Saves all prefs stored in the $_SESSION into the database.
*
* @access public
* @return bool True if prefs exist and were saved.
* @author Quinn Comendant
* @version 1.0
* @since 04 Jun 2006 17:19:56
*/
function save()
{
$app =& App::getInstance();
$db =& DB::getInstance();
// Skip this method if not using the db.
if (true !== $this->getParam('persistent')) {
return true;
}
// User_id must not be empty.
if ('' == $this->getParam('user_id')) {
$app->logMsg(sprintf('Cannot save prefs because user_id not set.', null), LOG_WARNING, __FILE__, __LINE__);
return false;
}
$this->initDB();
if (isset($_SESSION['_prefs'][$this->_ns]['persistent']) && is_array($_SESSION['_prefs'][$this->_ns]['persistent']) && !empty($_SESSION['_prefs'][$this->_ns]['persistent'])) {
// Delete old prefs from database.
$db->query("
DELETE FROM " . $db->escapeString($this->getParam('db_table')) . "
WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
");
// Insert new prefs.
$insert_values = array();
foreach ($_SESSION['_prefs'][$this->_ns]['persistent'] as $key => $val) {
$insert_values[] = sprintf("('%s', '%s', '%s', '%s')",
$db->escapeString($this->getParam('user_id')),
$db->escapeString($this->_ns),
$db->escapeString($key),
$db->escapeString(serialize($val))
);
}
// TODO: after MySQL 5.0.23 is released this query could benefit from INSERT DELAYED.
$db->query("
INSERT INTO " . $db->escapeString($this->getParam('db_table')) . "
(user_id, pref_namespace, pref_key, pref_value)
VALUES " . join(', ', $insert_values) . "
");
$app->logMsg(sprintf('Saved %s prefs to database.', sizeof($insert_values)), LOG_DEBUG, __FILE__, __LINE__);
return true;
}
return false;
}
}
?>