source: trunk/lib/Prefs.inc.php @ 470

Last change on this file since 470 was 468, checked in by anonymous, 10 years ago

Completed integrating /branches/eli_branch into /trunk. Changes include:

  • Removed closing ?> from end of files
  • Upgrade old-style contructor methods to use construct() instead.
  • Class properties and methods defined as public, private, static or protected
  • Ensure code runs under E_ALL with only mysql_* deprecated warnings
  • Search for the '@' symbol anywhere it might be used to supress runtime errors, then replace with proper error recovery.
  • Run the php cli -l option to check files for syntax errors.
  • Bring tests up-to-date with latest version and methods of PHPUnit
File size: 15.2 KB
RevLine 
[1]1<?php
2/**
[362]3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
[396]5 * Copyright 2001-2012 Strangecode, LLC
[462]6 *
[362]7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
[462]13 *
[362]14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
[462]18 *
[362]19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
[136]24 * Prefs.inc.php
[1]25 *
[462]26 * Prefs provides an API for saving arbitrary values in a user's session.
[146]27 * Session prefs can be stored into a database with the optional save() and load() methods.
[136]28 *
[1]29 * @author  Quinn Comendant <quinn@strangecode.com>
[136]30 * @version 2.1
[462]31 *
[150]32 * Example of use:
33---------------------------------------------------------------------
[151]34// Load preferences for the user's session.
[150]35require_once 'codebase/lib/Prefs.inc.php';
[153]36$prefs = new Prefs('my-namespace');
[150]37$prefs->setParam(array(
[152]38    'persistent' => $auth->isLoggedIn(),
[150]39    'user_id' => $auth->get('user_id'),
40));
41$prefs->setDefaults(array(
42    'search_num_results' => 25,
43    'datalog_num_entries' => 25,
44));
45$prefs->load();
[151]46
47// Update preferences. Make sure to validate this input first!
48$prefs->set('search_num_results', getFormData('search_num_results'));
49$prefs->set('datalog_num_entries', getFormData('datalog_num_entries'));
50$prefs->save();
51
[150]52---------------------------------------------------------------------
[1]53 */
54class Prefs {
55
[136]56    // Namespace of this instance of Prefs.
[468]57    private $_ns;
[1]58
[152]59    // Configuration parameters for this object.
[468]60    private $_params = array(
[462]61
[152]62        // Enable database storage. If this is false, all prefs will live only as long as the session.
63        'persistent' => false,
[462]64
[152]65        // The current user_id for which to load/save persistent preferences.
[149]66        'user_id' => null,
[462]67
[152]68        // How long before we force a reload of the persistent prefs data? 3600 = once every hour.
[149]69        'load_timeout' => 3600,
[462]70
[152]71        // Name of database table to store persistent prefs.
[147]72        'db_table' => 'pref_tbl',
[146]73
74        // Automatically create table and verify columns. Better set to false after site launch.
[396]75        // This value is overwritten by the $app->getParam('db_create_tables') setting if it is available.
[146]76        'create_table' => true,
77    );
78
[1]79    /**
80     * Prefs constructor.
81     */
[468]82    public function __construct($namespace='')
[1]83    {
[146]84        $app =& App::getInstance();
85
[154]86        $this->_ns = $namespace;
[462]87
[136]88        // Initialized the prefs array.
[463]89        if (!isset($_SESSION['_prefs'][$this->_ns]['saved'])) {
[150]90            $this->clear();
[136]91        }
[146]92
93        // Get create tables config from global context.
94        if (!is_null($app->getParam('db_create_tables'))) {
95            $this->setParam(array('create_table' => $app->getParam('db_create_tables')));
96        }
[1]97    }
98
99    /**
[146]100     * Setup the database table for this class.
101     *
102     * @access  public
103     * @author  Quinn Comendant <quinn@strangecode.com>
104     * @since   04 Jun 2006 16:41:42
105     */
[468]106    public function initDB($recreate_db=false)
[146]107    {
108        $app =& App::getInstance();
109        $db =& DB::getInstance();
110
111        static $_db_tested = false;
112
113        if ($recreate_db || !$_db_tested && $this->getParam('create_table')) {
114            if ($recreate_db) {
115                $db->query("DROP TABLE IF EXISTS " . $this->getParam('db_table'));
[201]116                $app->logMsg(sprintf('Dropping and recreating table %s.', $this->getParam('db_table')), LOG_INFO, __FILE__, __LINE__);
[146]117            }
118            $db->query("CREATE TABLE IF NOT EXISTS " . $db->escapeString($this->getParam('db_table')) . " (
119                user_id VARCHAR(32) NOT NULL DEFAULT '',
120                pref_namespace VARCHAR(32) NOT NULL DEFAULT '',
121                pref_key VARCHAR(64) NOT NULL DEFAULT '',
122                pref_value TEXT,
123                PRIMARY KEY (user_id, pref_namespace, pref_key)
124            )");
125
126            if (!$db->columnExists($this->getParam('db_table'), array(
127                'user_id',
128                'pref_namespace',
129                'pref_key',
130                'pref_value',
131            ), false, false)) {
132                $app->logMsg(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), LOG_ALERT, __FILE__, __LINE__);
133                trigger_error(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), E_USER_ERROR);
134            }
135        }
136        $_db_tested = true;
137    }
138
139    /**
140     * Set the params of this object.
141     *
142     * @param  array $params   Array of param keys and values to set.
143     */
[468]144    public function setParam($params=null)
[146]145    {
146        if (isset($params) && is_array($params)) {
147            // Merge new parameters with old overriding only those passed.
148            $this->_params = array_merge($this->_params, $params);
149        }
150    }
151
152    /**
153     * Return the value of a parameter, if it exists.
154     *
155     * @access public
156     * @param string $param        Which parameter to return.
157     * @return mixed               Configured parameter value.
158     */
[468]159    public function getParam($param)
[146]160    {
161        $app =& App::getInstance();
[462]162
[146]163        if (isset($this->_params[$param])) {
164            return $this->_params[$param];
165        } else {
166            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
167            return null;
168        }
169    }
170
171    /**
[462]172     * Sets the default values for preferences. If a preference is not explicitly
[151]173     * set, the value set here will be used. Can be called multiple times to merge additional
174     * defaults together.
[1]175     *
[462]176     * @param  array $defaults  Array of key-value pairs
[1]177     */
[468]178    public function setDefaults($defaults)
[1]179    {
[146]180        if (isset($defaults) && is_array($defaults)) {
[154]181            $_SESSION['_prefs'][$this->_ns]['defaults'] = array_merge($_SESSION['_prefs'][$this->_ns]['defaults'], $defaults);
[1]182        }
183    }
184
185    /**
[151]186     * Store a key-value pair in the session. If the value is different than what is set by setDefaults
187     * the value will be scheduled to be saved in the database.
[275]188     * This function determines what data is saved to the database. Ensure clean values!
[1]189     *
[152]190     * @param  string $key          The name of the preference to modify.
191     * @param  string $val          The new value for this preference.
192     * @param  bool   $persistent   Save this value forever? Set to false and value will exist as long as the session is in use.
[1]193     */
[468]194    public function set($key, $val)
[1]195    {
[151]196        $app =& App::getInstance();
[152]197
[151]198        if ('' == $key) {
199            $app->logMsg(sprintf('Key is empty (provided with value: %s)', $val), LOG_NOTICE, __FILE__, __LINE__);
200            return false;
[149]201        }
[462]202
203        // Set a saved preference if...
[159]204        // - there isn't a default.
[462]205        // - or the new value is different than the default
206        // - or there is a previously existing saved key.
207        if (!isset($_SESSION['_prefs'][$this->_ns]['defaults'][$key])
208        || $_SESSION['_prefs'][$this->_ns]['defaults'][$key] != $val
209        || isset($_SESSION['_prefs'][$this->_ns]['saved'][$key])) {
210            $_SESSION['_prefs'][$this->_ns]['saved'][$key] = $val;
[331]211            $app->logMsg(sprintf('Setting preference %s => %s', $key, truncate(getDump($val, true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[159]212        } else {
[331]213            $app->logMsg(sprintf('Not setting preference %s => %s', $key, truncate(getDump($val, true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[151]214        }
[1]215    }
[42]216
[1]217    /**
[462]218     * Returns the value of the requested preference. Saved values take precedence, but if none is set
[151]219     * a default value is returned, or if not that, null.
[1]220     *
[136]221     * @param string $key       The name of the preference to retrieve.
[1]222     *
223     * @return string           The value of the preference.
224     */
[468]225    public function get($key)
[1]226    {
[151]227        $app =& App::getInstance();
[462]228        if (isset($_SESSION['_prefs'][$this->_ns]['saved']) && array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['saved'])) {
229            $app->logMsg(sprintf('Found %s in saved', $key), LOG_DEBUG, __FILE__, __LINE__);
230            return $_SESSION['_prefs'][$this->_ns]['saved'][$key];
[242]231        } else if (isset($_SESSION['_prefs'][$this->_ns]['defaults']) && array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['defaults'])) {
[241]232            $app->logMsg(sprintf('Found %s in defaults', $key), LOG_DEBUG, __FILE__, __LINE__);
[154]233            return $_SESSION['_prefs'][$this->_ns]['defaults'][$key];
[151]234        } else {
[154]235            $app->logMsg(sprintf('Key not found in prefs cache: %s', $key), LOG_DEBUG, __FILE__, __LINE__);
[151]236            return null;
237        }
[1]238    }
[42]239
[1]240    /**
[152]241     * To see if a preference has been set.
[1]242     *
[136]243     * @param string $key       The name of the preference to check.
[151]244     * @return boolean          True if the preference isset and not empty false otherwise.
[1]245     */
[468]246    public function exists($key)
[1]247    {
[462]248        return array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['saved']);
[1]249    }
[42]250
[1]251    /**
[462]252     * Clear a set preference value. This will also remove the value from the database.
[1]253     *
[146]254     * @param string $key       The name of the preference to delete.
[1]255     */
[468]256    public function delete($key)
[1]257    {
[462]258        unset($_SESSION['_prefs'][$this->_ns]['saved'][$key]);
[1]259    }
260
261    /**
[462]262     * Resets the $_SESSION cache. This should be executed with the same consideration
[151]263     * as $auth->clear(), such as when logging out.
[1]264     */
[468]265    public function clear($focus='all')
[1]266    {
[241]267        switch ($focus) {
268        case 'all' :
269            $_SESSION['_prefs'][$this->_ns] = array(
270                'loaded' => false,
271                'load_datetime' => '1970-01-01',
272                'defaults' => array(),
[462]273                'saved' => array(),
[241]274            );
275            break;
276
277        case 'defaults' :
278            $_SESSION['_prefs'][$this->_ns]['defaults'] = array();
279            break;
280
[462]281        case 'saved' :
282            $_SESSION['_prefs'][$this->_ns]['saved'] = array();
[241]283            break;
284        }
[1]285    }
[462]286
[146]287    /*
[151]288    * Retrieves all prefs from the database and stores them in the $_SESSION.
[146]289    *
290    * @access   public
[150]291    * @param    bool    $force  Set to always load from database, regardless if _isLoaded() or not.
[146]292    * @return   bool    True if loading succeeded.
293    * @author   Quinn Comendant <quinn@strangecode.com>
294    * @version  1.0
295    * @since    04 Jun 2006 16:56:53
296    */
[468]297    public function load($force=false)
[146]298    {
299        $app =& App::getInstance();
300        $db =& DB::getInstance();
[462]301
[146]302        // Skip this method if not using the db.
[152]303        if (true !== $this->getParam('persistent')) {
[146]304            return true;
305        }
306
[150]307        $this->initDB();
308
[146]309        // Prefs already loaded for this session.
[150]310        if (!$force && $this->_isLoaded()) {
[146]311            return true;
312        }
313
314        // User_id must not be empty.
315        if ('' == $this->getParam('user_id')) {
[151]316            $app->logMsg(sprintf('Cannot save prefs because user_id not set.', null), LOG_WARNING, __FILE__, __LINE__);
[146]317            return false;
318        }
[462]319
[150]320        // Clear existing cache.
[462]321        $this->clear('saved');
322
[151]323        // Retrieve all prefs for this user and namespace.
[146]324        $qid = $db->query("
325            SELECT pref_key, pref_value
326            FROM " . $db->escapeString($this->getParam('db_table')) . "
327            WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
328            AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
329            LIMIT 10000
330        ");
331        while (list($key, $val) = mysql_fetch_row($qid)) {
[462]332            $_SESSION['_prefs'][$this->_ns]['saved'][$key] = unserialize($val);
[146]333        }
[462]334
[241]335        $app->logMsg(sprintf('Loaded %s prefs from database.', mysql_num_rows($qid)), LOG_DEBUG, __FILE__, __LINE__);
[462]336
[146]337        // Data loaded only once per session.
[154]338        $_SESSION['_prefs'][$this->_ns]['loaded'] = true;
339        $_SESSION['_prefs'][$this->_ns]['load_datetime'] = date('Y-m-d H:i:s');
[462]340
[146]341        return true;
342    }
[462]343
[146]344    /*
[149]345    * Returns true if the prefs had been loaded from the database into the $_SESSION recently.
346    * This function is simply a check so the database isn't access every page load.
[462]347    *
[146]348    * @access   private
349    * @return   bool    True if prefs are loaded.
350    * @author   Quinn Comendant <quinn@strangecode.com>
351    * @version  1.0
352    * @since    04 Jun 2006 17:12:44
353    */
[468]354    private function _isLoaded()
[146]355    {
[154]356        if (isset($_SESSION['_prefs'][$this->_ns]['load_datetime'])
357        && strtotime($_SESSION['_prefs'][$this->_ns]['load_datetime']) > time() - $this->getParam('load_timeout')
[462]358        && isset($_SESSION['_prefs'][$this->_ns]['loaded'])
[154]359        && true === $_SESSION['_prefs'][$this->_ns]['loaded']) {
[149]360            return true;
361        } else {
362            return false;
363        }
[146]364    }
[462]365
[146]366    /*
367    * Saves all prefs stored in the $_SESSION into the database.
368    *
369    * @access   public
370    * @return   bool    True if prefs exist and were saved.
371    * @author   Quinn Comendant <quinn@strangecode.com>
372    * @version  1.0
373    * @since    04 Jun 2006 17:19:56
374    */
[468]375    public function save()
[146]376    {
377        $app =& App::getInstance();
378        $db =& DB::getInstance();
[462]379
[146]380        // Skip this method if not using the db.
[152]381        if (true !== $this->getParam('persistent')) {
[146]382            return true;
383        }
[462]384
[146]385        // User_id must not be empty.
386        if ('' == $this->getParam('user_id')) {
[151]387            $app->logMsg(sprintf('Cannot save prefs because user_id not set.', null), LOG_WARNING, __FILE__, __LINE__);
[146]388            return false;
389        }
390
391        $this->initDB();
392
[462]393        if (isset($_SESSION['_prefs'][$this->_ns]['saved']) && is_array($_SESSION['_prefs'][$this->_ns]['saved']) && !empty($_SESSION['_prefs'][$this->_ns]['saved'])) {
[146]394            // Delete old prefs from database.
395            $db->query("
396                DELETE FROM " . $db->escapeString($this->getParam('db_table')) . "
397                WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
398                AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
399            ");
[462]400
[146]401            // Insert new prefs.
402            $insert_values = array();
[462]403            foreach ($_SESSION['_prefs'][$this->_ns]['saved'] as $key => $val) {
404                $insert_values[] = sprintf("('%s', '%s', '%s', '%s')",
405                    $db->escapeString($this->getParam('user_id')),
406                    $db->escapeString($this->_ns),
407                    $db->escapeString($key),
[158]408                    $db->escapeString(serialize($val))
409                );
[146]410            }
[159]411            // TODO: after MySQL 5.0.23 is released this query could benefit from INSERT DELAYED.
[146]412            $db->query("
[462]413                INSERT INTO " . $db->escapeString($this->getParam('db_table')) . "
[146]414                (user_id, pref_namespace, pref_key, pref_value)
415                VALUES " . join(', ', $insert_values) . "
416            ");
[462]417
[151]418            $app->logMsg(sprintf('Saved %s prefs to database.', sizeof($insert_values)), LOG_DEBUG, __FILE__, __LINE__);
[146]419            return true;
420        }
[462]421
[146]422        return false;
423    }
[1]424}
425
Note: See TracBrowser for help on using the repository browser.