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

Last change on this file since 152 was 152, checked in by scdev, 18 years ago

Q - In the middle of working on the Prefs and Cache instantiation mode...can't decide to use singleton pattern or global vars. Updated ImageThumb? to allow filenames with path elements such as 01/23/4567_file.jpg.

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