Source for file _abstractsettings.class.php
Documentation is available at _abstractsettings.class.php
* This file implements the AbstractSettings class designed to handle any kind of settings.
* This file is part of Quam Plures - {@link http://quamplures.net/}
* See also {@link https://launchpad.net/quam-plures}.
* @copyright (c) 2009 - 2011 by the Quam Plures developers - {@link http://quamplures.net/}
* @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/}
* Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
* {@internal License choice
* - If you have received this file as part of a package, please find the license.txt file in
* the same folder or the closest folder above for complete license terms.
* - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
* then you must choose one of the following licenses before using the file:
* - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
* - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
* {@internal Open Source relicensing agreement:
* Daniel HAHLER grants Francois PLANQUE the right to license
* Daniel HAHLER's contributions to this file and the b2evolution project
* under any OSI approved OSS license (http://www.opensource.org/licenses/).
* {@internal Below is a list of authors who have contributed to design/coding of this file: }}
* @author blueyed: Daniel HAHLER
* @author fplanque: Francois PLANQUE
if( !defined('QP_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
* Class to handle settings in an abstract manner (to get used with either 1, 2 or 3 DB column keys).
* Arrays and Objects automatically get serialized and unserialized
* (in {@link AbstractSettings::get()} and {@link AbstractSettings::dbupdate()}).
* Note: I've evaluated splitting this into single classes for performance reasons, but only
* get() is relevant performance-wise and we could now only get rid of the switch() therein,
* which is not sufficient to split it into *_base + _X classes. (blueyed, 2006-08)
* @see UserSettings, GeneralSettings, PluginSettings, CollectionSettings
* The DB table which stores the settings.
* Array with DB column key names.
* DB column name for the value.
* The number of column keys to cache by. This are the first x keys of
* {@link $col_key_names}. 0 means 'load all'.
* false, if settings could not be loaded or NULL if not initialized.
* Do we have loaded everything?
* Maps last colkeyname to some default setting that will be used by
* {@link get()} if no value was defined (and it is set as a default).
* @param string The name of the DB table with the settings stored.
* @param array List of names for the DB columns keys that reference a value.
* @param string The name of the DB column that holds the value.
* @param integer The number of column keys to cache by. This are the first x keys of {@link $col_key_names}. 0 means 'load all'.
function AbstractSettings( $db_table_name, $col_key_names, $col_value_name, $cache_by_col_keys = 0 )
* internal counter for the number of column keys
if( $this->count_col_key_names > 3 || $this->count_col_key_names < 1 )
* Load all settings, disregarding the derived classes setting of
* {@link $cache_by_col_keys} - useful if you know that you want to get
* all user settings for example.
* Loads the settings. Not meant to be called directly, but gets called
* @param string First column key
* @param string Second column key
* @param string Third column key
* @return boolean always true
function _load( $arg1 = NULL, $arg2 = NULL, $arg3 = NULL )
* The where clause - gets filled when {@link $cache_by_col_keys} is used.
{ // The number of column keys to cache by is > 0
$testCache = $this->cache;
$args = array( $arg1, $arg2, $arg3 );
|| ! isset ( $testCache[$args[$i]] )
|| ! ($testCache = & $testCache[$args[$i]]) )
{ // We cache everything at once!
$result = $DB->get_results( '
? ' WHERE '. implode( ' AND ', $whereList )
switch( $this->count_col_key_names )
{ // Remember that we've tried it
$this->cache[ $arg1 ] = NULL;
else foreach( $result as $loop_row )
{ // Remember that we've tried it
$this->cache[ $arg1 ][ $arg2 ] = NULL;
else foreach( $result as $loop_row )
{ // Remember that we've tried it
$this->cache[ $arg1 ][ $arg2 ][ $arg3 ] = NULL;
else foreach( $result as $loop_row )
* Get a setting from the DB settings table.
* @param string First column key
* @param string Second column key
* @param string Third column key
* @return string|false|NULLvalue as string on success; NULL if not found; false in case of error
function get( $col_key1, $col_key2 = NULL, $col_key3 = NULL )
$Timer->resume('abstractsettings_'. $this_class. '_get');
switch( $this->count_col_key_names )
$this->_load( $col_key1 );
if( !empty($this->cache[ $col_key1 ]->unserialized) )
{ // The value has been unserialized before:
$r = $this->cache[ $col_key1 ]->value;
elseif( isset ($this->cache[ $col_key1 ]->value) )
{ // First attempt to access the value, we need to unserialize it:
// Try to unserialize setting (once) - this is as fast as checking an array of values that should get unserialized
$this->cache[ $col_key1 ]->value = $r;
$r = $this->cache[ $col_key1 ]->value;
$this->cache[ $col_key1 ]->unserialized = true;
{ // The value is not in the cache, we use the default:
$this->cache[ $col_key1 ]->value = $r; // remember in cache
$this->cache[ $col_key1 ]->dbUptodate = true;
$this->cache[ $col_key1 ]->unserialized = true;
$from_default = true; // for debug
$this->_load( $col_key1, $col_key2 );
if( isset ($this->cache[ $col_key1 ][ $col_key2 ]->unserialized) )
$r = $this->cache[ $col_key1 ][ $col_key2 ]->value;
elseif( isset ($this->cache[ $col_key1 ][ $col_key2 ]->value) )
// Try to unserialize setting (once) - this is as fast as checking an array of values that should get unserialized
if( ($r = @unserialize($this->cache[ $col_key1 ][ $col_key2 ]->value)) !== false )
$this->cache[ $col_key1 ][ $col_key2 ]->value = $r;
$r = $this->cache[ $col_key1 ][ $col_key2 ]->value;
$this->cache[ $col_key1 ][ $col_key2 ]->unserialized = true;
$this->cache[ $col_key1 ][ $col_key2 ]->value = $r; // remember in cache
$this->cache[ $col_key1 ][ $col_key2 ]->dbUptodate = true;
$this->cache[ $col_key1 ][ $col_key2 ]->unserialized = true;
$from_default = true; // for debug
$this->_load( $col_key1, $col_key2, $col_key3 );
if( isset ($this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->unserialized) )
$r = $this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->value;
elseif( isset ($this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->value) )
// Try to unserialize setting (once) - this is as fast as checking an array of values that should get unserialized
if( ($r = @unserialize($this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->value)) !== false )
$this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->value = $r;
$r = $this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->value;
$this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->unserialized = true;
$this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->value = $r; // remember in cache
$this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->dbUptodate = true;
$this->cache[ $col_key1 ][ $col_key2 ][ $col_key3 ]->unserialized = true;
$from_default = true; // for debug
$Debuglog->add( $this_class. '::get( '. $col_key1. '/'. $col_key2. '/'. $col_key3. ' ): '
. ( isset ($from_default) ? '[DEFAULT]: ' : '' )
$Timer->pause('abstractsettings_'. $this_class. '_get');
* Get the default for the last key of {@link $col_key_names}
* @param string The last column key
* @return NULL|mixedNULL if no default is set, otherwise the value (should be string).
* Only set the first variable (passed by reference) if we could retrieve a
* @param mixed variable to set maybe (by reference)
* @param string the values for the column keys (depends on $this->col_key_names
* and must match its count and order)
* @return boolean true on success (variable was set), false if not
if( $result !== NULL && $result !== false )
{ // No error and value retrieved
* Temporarily sets a setting ({@link dbupdate()} writes it to DB).
* @param string $args,... the values for the {@link $col_key_names column keys}
* and {@link $col_value_name column value}. Must match order and count!
* @return boolean true, if the value has been set, false if it has not changed.
switch( $this->count_col_key_names )
$atCache = & $this->cache[ $args[0] ];
$atCache = & $this->cache[ $args[0] ][ $args[1] ];
$atCache = & $this->cache[ $args[0] ][ $args[1] ][ $args[2] ];
$atCache->dbRemove = false;
if( isset ($atCache->value) )
if( $atCache->value == $value )
$Debuglog->add( $debugMsg. ' Already set to the same value.', 'settings' );
$atCache->value = $value;
$atCache->dbUptodate = false;
$atCache->unserialized = false; // We haven't tried to unserialize the value yet
$Debuglog->add( $debugMsg. ' SET!', 'settings' );
* Set an array of values.
* @param array Array of parameters for {@link set()}
foreach( $array as $lSet )
* @param array List of {@link $col_key_names}
switch( $this->count_col_key_names )
$atCache = & $this->cache[ $args[0] ];
$atCache = & $this->cache[ $args[0] ][ $args[1] ];
$atCache = & $this->cache[ $args[0] ][ $args[1] ][ $args[2] ];
$atCache->dbRemove = true;
unset ($atCache->unserialized);
* Delete an array of values.
* @param array Array of parameters for {@link delete()}
foreach( $array as $lDel )
* Delete values for {@link $_defaults default settings} in DB.
* This will use the default settings on the next {@link get()}
* @return boolean true, if settings have been updated; false otherwise
* Commit changed settings to DB.
* @return boolean true, if settings have been updated; false otherwise
if( empty($this->cache) )
$query_where_delete = array();
switch( $this->count_col_key_names )
foreach( $this->cache as $key => $value )
{ // Remembered as not existing
if( ! empty($value->dbRemove) )
$query_where_delete[] = "{ $this->col_key_names[0]} = '$key'";
unset ( $this->cache[$key] );
elseif( isset ($value->dbUptodate) && !$value->dbUptodate )
$query_insert[] = "('$key', '". $DB->escape( $value ). "')";
$this->cache[$key]->dbUptodate = true;
foreach( $this->cache as $key => $value )
foreach( $value as $key2 => $value2 )
{ // Remembered as not existing
if( ! empty($value2->dbRemove) )
$query_where_delete[] = "{ $this->col_key_names[0]} = ' $key' AND { $this->col_key_names[1]} = ' $key2' ";
unset ( $this->cache[$key][$key2] );
elseif( isset ($value2->dbUptodate) && !$value2->dbUptodate )
$value2 = $value2->value;
$query_insert[] = "('$key', '$key2', '". $DB->escape( $value2 ). "')";
$this->cache[$key][$key2]->dbUptodate = true;
foreach( $this->cache as $key => $value )
foreach( $value as $key2 => $value2 )
foreach( $value2 as $key3 => $value3 )
{ // Remembered as not existing
if( ! empty($value3->dbRemove) )
$query_where_delete[] = "{ $this->col_key_names[0]} = ' $key' AND { $this->col_key_names[1]} = ' $key2' AND { $this->col_key_names[2]} = ' $key3' ";
unset ( $this->cache[$key][$key2][$key3] );
elseif( isset ($value3->dbUptodate) && !$value3->dbUptodate )
$value3 = $value3->value;
$query_insert[] = "('$key', '$key2', '$key3', '". $DB->escape( $value3 ). "')";
$this->cache[$key][$key2][$key3]->dbUptodate = true;
if( ! empty($query_where_delete) )
$query = 'DELETE FROM '. $this->db_table_name. " WHERE\n(". implode( ")\nOR (", $query_where_delete ). ')';
$r = (boolean) $DB->query( $query );
if( ! empty($query_insert) )
. ') VALUES '. implode(', ', $query_insert);
$r = $DB->query( $query ) || $r;
* Reset cache (includes settings to be written to DB).
* This is useful, to rollback settings that have been made, e.g. when a Plugin
* decides that his settings should not get updated.
|