source: tags/2.1.5/lib/Prefs.inc.php

Last change on this file was 377, checked in by quinn, 14 years ago

Releasing trunk as stable version 2.1.5

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/>
[376]5 * Copyright 2001-2010 Strangecode, LLC
[362]6 *
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.
13 *
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.
18 *
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 *
[146]26 * Prefs provides an API for saving arbitrary values in a user's session.
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
[150]31 *
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.
57    var $_ns;
[1]58
[152]59    // Configuration parameters for this object.
[146]60    var $_params = array(
61       
[152]62        // Enable database storage. If this is false, all prefs will live only as long as the session.
63        'persistent' => false,
64       
65        // The current user_id for which to load/save persistent preferences.
[149]66        'user_id' => null,
67       
[152]68        // How long before we force a reload of the persistent prefs data? 3600 = once every hour.
[149]69        'load_timeout' => 3600,
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.
75        'create_table' => true,
76    );
77
[1]78    /**
79     * Prefs constructor.
80     */
[136]81    function Prefs($namespace='')
[1]82    {
[146]83        $app =& App::getInstance();
84
[154]85        $this->_ns = $namespace;
[136]86       
87        // Initialized the prefs array.
[154]88        if (!isset($_SESSION['_prefs'][$this->_ns])) {
[150]89            $this->clear();
[136]90        }
[146]91
92        // Get create tables config from global context.
93        if (!is_null($app->getParam('db_create_tables'))) {
94            $this->setParam(array('create_table' => $app->getParam('db_create_tables')));
95        }
[1]96    }
97
98    /**
[146]99     * Setup the database table for this class.
100     *
101     * @access  public
102     * @author  Quinn Comendant <quinn@strangecode.com>
103     * @since   04 Jun 2006 16:41:42
104     */
105    function initDB($recreate_db=false)
106    {
107        $app =& App::getInstance();
108        $db =& DB::getInstance();
109
110        static $_db_tested = false;
111
112        if ($recreate_db || !$_db_tested && $this->getParam('create_table')) {
113            if ($recreate_db) {
114                $db->query("DROP TABLE IF EXISTS " . $this->getParam('db_table'));
[201]115                $app->logMsg(sprintf('Dropping and recreating table %s.', $this->getParam('db_table')), LOG_INFO, __FILE__, __LINE__);
[146]116            }
117            $db->query("CREATE TABLE IF NOT EXISTS " . $db->escapeString($this->getParam('db_table')) . " (
118                user_id VARCHAR(32) NOT NULL DEFAULT '',
119                pref_namespace VARCHAR(32) NOT NULL DEFAULT '',
120                pref_key VARCHAR(64) NOT NULL DEFAULT '',
121                pref_value TEXT,
122                PRIMARY KEY (user_id, pref_namespace, pref_key)
123            )");
124
125            if (!$db->columnExists($this->getParam('db_table'), array(
126                'user_id',
127                'pref_namespace',
128                'pref_key',
129                'pref_value',
130            ), false, false)) {
131                $app->logMsg(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), LOG_ALERT, __FILE__, __LINE__);
132                trigger_error(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), E_USER_ERROR);
133            }
134        }
135        $_db_tested = true;
136    }
137
138    /**
139     * Set the params of this object.
140     *
141     * @param  array $params   Array of param keys and values to set.
142     */
143    function setParam($params=null)
144    {
145        if (isset($params) && is_array($params)) {
146            // Merge new parameters with old overriding only those passed.
147            $this->_params = array_merge($this->_params, $params);
148        }
149    }
150
151    /**
152     * Return the value of a parameter, if it exists.
153     *
154     * @access public
155     * @param string $param        Which parameter to return.
156     * @return mixed               Configured parameter value.
157     */
158    function getParam($param)
159    {
160        $app =& App::getInstance();
161   
162        if (isset($this->_params[$param])) {
163            return $this->_params[$param];
164        } else {
165            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
166            return null;
167        }
168    }
169
170    /**
[151]171     * Sets the default values for preferences. If a preference is not explicitly
172     * set, the value set here will be used. Can be called multiple times to merge additional
173     * defaults together.
[1]174     *
[151]175     * @param  array $defaults  Array of key-value pairs
[1]176     */
[146]177    function setDefaults($defaults)
[1]178    {
[146]179        if (isset($defaults) && is_array($defaults)) {
[154]180            $_SESSION['_prefs'][$this->_ns]['defaults'] = array_merge($_SESSION['_prefs'][$this->_ns]['defaults'], $defaults);
[1]181        }
182    }
183
184    /**
[151]185     * Store a key-value pair in the session. If the value is different than what is set by setDefaults
186     * the value will be scheduled to be saved in the database.
[275]187     * This function determines what data is saved to the database. Ensure clean values!
[1]188     *
[152]189     * @param  string $key          The name of the preference to modify.
190     * @param  string $val          The new value for this preference.
191     * @param  bool   $persistent   Save this value forever? Set to false and value will exist as long as the session is in use.
[1]192     */
[136]193    function set($key, $val)
[1]194    {
[151]195        $app =& App::getInstance();
[152]196
[151]197        if ('' == $key) {
198            $app->logMsg(sprintf('Key is empty (provided with value: %s)', $val), LOG_NOTICE, __FILE__, __LINE__);
199            return false;
[149]200        }
[152]201       
[334]202        // Set a persistent preference if...
[159]203        // - there isn't a default.
204        // - the new value is different than the default
[254]205        // - there is a previously existing persistent key.
[154]206        if (!isset($_SESSION['_prefs'][$this->_ns]['defaults'][$key]) || $_SESSION['_prefs'][$this->_ns]['defaults'][$key] != $val || isset($_SESSION['_prefs'][$this->_ns]['persistent'][$key])) {
207            $_SESSION['_prefs'][$this->_ns]['persistent'][$key] = $val;           
[331]208            $app->logMsg(sprintf('Setting preference %s => %s', $key, truncate(getDump($val, true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[159]209        } else {
[331]210            $app->logMsg(sprintf('Not setting preference %s => %s', $key, truncate(getDump($val, true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[151]211        }
[1]212    }
[42]213
[1]214    /**
[151]215     * Returns the value of the requested preference. Persistent values take precedence, but if none is set
216     * a default value is returned, or if not that, null.
[1]217     *
[136]218     * @param string $key       The name of the preference to retrieve.
[1]219     *
220     * @return string           The value of the preference.
221     */
[136]222    function get($key)
[1]223    {
[151]224        $app =& App::getInstance();
[242]225        if (isset($_SESSION['_prefs'][$this->_ns]['persistent']) && array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['persistent'])) {
[241]226            $app->logMsg(sprintf('Found %s in persistent', $key), LOG_DEBUG, __FILE__, __LINE__);
[154]227            return $_SESSION['_prefs'][$this->_ns]['persistent'][$key];
[242]228        } else if (isset($_SESSION['_prefs'][$this->_ns]['defaults']) && array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['defaults'])) {
[241]229            $app->logMsg(sprintf('Found %s in defaults', $key), LOG_DEBUG, __FILE__, __LINE__);
[154]230            return $_SESSION['_prefs'][$this->_ns]['defaults'][$key];
[151]231        } else {
[154]232            $app->logMsg(sprintf('Key not found in prefs cache: %s', $key), LOG_DEBUG, __FILE__, __LINE__);
[151]233            return null;
234        }
[1]235    }
[42]236
[1]237    /**
[152]238     * To see if a preference has been set.
[1]239     *
[136]240     * @param string $key       The name of the preference to check.
[151]241     * @return boolean          True if the preference isset and not empty false otherwise.
[1]242     */
[136]243    function exists($key)
[1]244    {
[154]245        return array_key_exists($key, $_SESSION['_prefs'][$this->_ns]['persistent']);
[1]246    }
[42]247
[1]248    /**
[151]249     * Clear a set preference value. This will also remove the value from the database.
[1]250     *
[146]251     * @param string $key       The name of the preference to delete.
[1]252     */
[136]253    function delete($key)
[1]254    {
[154]255        unset($_SESSION['_prefs'][$this->_ns]['persistent'][$key]);
[1]256    }
257
258    /**
[151]259     * Resets the $_SESSION cache. This should be executed with the same consideration
260     * as $auth->clear(), such as when logging out.
[1]261     */
[241]262    function clear($focus='all')
[1]263    {
[241]264        switch ($focus) {
265        case 'all' :
266            $_SESSION['_prefs'][$this->_ns] = array(
267                'loaded' => false,
268                'load_datetime' => '1970-01-01',
269                'defaults' => array(),
270                'persistent' => array(),
271            );
272            break;
273
274        case 'defaults' :
275            $_SESSION['_prefs'][$this->_ns]['defaults'] = array();
276            break;
277
278        case 'persistent' :
279            $_SESSION['_prefs'][$this->_ns]['persistent'] = array();
280            break;
281        }
[1]282    }
[146]283   
284    /*
[151]285    * Retrieves all prefs from the database and stores them in the $_SESSION.
[146]286    *
287    * @access   public
[150]288    * @param    bool    $force  Set to always load from database, regardless if _isLoaded() or not.
[146]289    * @return   bool    True if loading succeeded.
290    * @author   Quinn Comendant <quinn@strangecode.com>
291    * @version  1.0
292    * @since    04 Jun 2006 16:56:53
293    */
[150]294    function load($force=false)
[146]295    {
296        $app =& App::getInstance();
297        $db =& DB::getInstance();
298       
299        // Skip this method if not using the db.
[152]300        if (true !== $this->getParam('persistent')) {
[146]301            return true;
302        }
303
[150]304        $this->initDB();
305
[146]306        // Prefs already loaded for this session.
[150]307        if (!$force && $this->_isLoaded()) {
[146]308            return true;
309        }
310
311        // User_id must not be empty.
312        if ('' == $this->getParam('user_id')) {
[151]313            $app->logMsg(sprintf('Cannot save prefs because user_id not set.', null), LOG_WARNING, __FILE__, __LINE__);
[146]314            return false;
315        }
316       
[150]317        // Clear existing cache.
[241]318        $this->clear('persistent');
[150]319       
[151]320        // Retrieve all prefs for this user and namespace.
[146]321        $qid = $db->query("
322            SELECT pref_key, pref_value
323            FROM " . $db->escapeString($this->getParam('db_table')) . "
324            WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
325            AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
326            LIMIT 10000
327        ");
328        while (list($key, $val) = mysql_fetch_row($qid)) {
[158]329            $_SESSION['_prefs'][$this->_ns]['persistent'][$key] = unserialize($val);
[146]330        }
331       
[241]332        $app->logMsg(sprintf('Loaded %s prefs from database.', mysql_num_rows($qid)), LOG_DEBUG, __FILE__, __LINE__);
[150]333       
[146]334        // Data loaded only once per session.
[154]335        $_SESSION['_prefs'][$this->_ns]['loaded'] = true;
336        $_SESSION['_prefs'][$this->_ns]['load_datetime'] = date('Y-m-d H:i:s');
[146]337       
338        return true;
339    }
340   
341    /*
[149]342    * Returns true if the prefs had been loaded from the database into the $_SESSION recently.
343    * This function is simply a check so the database isn't access every page load.
[146]344    *
345    * @access   private
346    * @return   bool    True if prefs are loaded.
347    * @author   Quinn Comendant <quinn@strangecode.com>
348    * @version  1.0
349    * @since    04 Jun 2006 17:12:44
350    */
351    function _isLoaded()
352    {
[154]353        if (isset($_SESSION['_prefs'][$this->_ns]['load_datetime'])
354        && strtotime($_SESSION['_prefs'][$this->_ns]['load_datetime']) > time() - $this->getParam('load_timeout')
355        && isset($_SESSION['_prefs'][$this->_ns]['loaded']) 
356        && true === $_SESSION['_prefs'][$this->_ns]['loaded']) {
[149]357            return true;
358        } else {
359            return false;
360        }
[146]361    }
362   
363    /*
364    * Saves all prefs stored in the $_SESSION into the database.
365    *
366    * @access   public
367    * @return   bool    True if prefs exist and were saved.
368    * @author   Quinn Comendant <quinn@strangecode.com>
369    * @version  1.0
370    * @since    04 Jun 2006 17:19:56
371    */
372    function save()
373    {
374        $app =& App::getInstance();
375        $db =& DB::getInstance();
376       
377        // Skip this method if not using the db.
[152]378        if (true !== $this->getParam('persistent')) {
[146]379            return true;
380        }
381       
382        // User_id must not be empty.
383        if ('' == $this->getParam('user_id')) {
[151]384            $app->logMsg(sprintf('Cannot save prefs because user_id not set.', null), LOG_WARNING, __FILE__, __LINE__);
[146]385            return false;
386        }
387
388        $this->initDB();
389
[159]390        if (isset($_SESSION['_prefs'][$this->_ns]['persistent']) && is_array($_SESSION['_prefs'][$this->_ns]['persistent']) && !empty($_SESSION['_prefs'][$this->_ns]['persistent'])) {
[146]391            // Delete old prefs from database.
392            $db->query("
393                DELETE FROM " . $db->escapeString($this->getParam('db_table')) . "
394                WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
395                AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
396            ");
397           
398            // Insert new prefs.
399            $insert_values = array();
[154]400            foreach ($_SESSION['_prefs'][$this->_ns]['persistent'] as $key => $val) {
[158]401                $insert_values[] = sprintf("('%s', '%s', '%s', '%s')", 
402                    $db->escapeString($this->getParam('user_id')), 
403                    $db->escapeString($this->_ns), 
404                    $db->escapeString($key), 
405                    $db->escapeString(serialize($val))
406                );
[146]407            }
[159]408            // TODO: after MySQL 5.0.23 is released this query could benefit from INSERT DELAYED.
[146]409            $db->query("
[159]410                INSERT INTO " . $db->escapeString($this->getParam('db_table')) . "
[146]411                (user_id, pref_namespace, pref_key, pref_value)
412                VALUES " . join(', ', $insert_values) . "
413            ");
414           
[151]415            $app->logMsg(sprintf('Saved %s prefs to database.', sizeof($insert_values)), LOG_DEBUG, __FILE__, __LINE__);
[146]416            return true;
417        }
418       
419        return false;
420    }
[1]421}
422
423
424?>
Note: See TracBrowser for help on using the repository browser.