* Copyright 2001-2010 Strangecode, LLC * * 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; } } ?>