Index: trunk/lib/Prefs.inc.php
===================================================================
--- trunk/lib/Prefs.inc.php	(revision 642)
+++ trunk/lib/Prefs.inc.php	(revision 644)
@@ -96,4 +96,7 @@
         // This value is overwritten by the $app->getParam('db_create_tables') setting if it is available.
         'create_table' => true,
+
+        // Original namespace set during __construct().
+        // 'namespace' => '',
     );
 
@@ -106,4 +109,7 @@
 
         $this->_ns = $namespace;
+
+        // Save the original namespace for the DB pref_namespace column. $this->_ns, used in the SESSION variable, will change based on the user_id.
+        $this->setParam(array('namespace' => $namespace));
 
         // Get create tables config from global context.
@@ -114,9 +120,4 @@
         // Optional initial params.
         $this->setParam($params);
-
-        // Initialized the prefs array.
-        if ('cookie' != $app->getParam('storagetype') && !isset($_SESSION['_prefs'][$this->_ns]['saved'])) {
-            $this->clear();
-        }
 
         // Run Prefs->save() upon script completion if we're using the database storagetype.
@@ -191,4 +192,23 @@
         }
 
+        // Append the user_id to the namespace to keep separate collections for different users.
+        if (isset($params['user_id']) && $params['user_id']) {
+            // $this->getParam('namespace') should always be available since it is set in __construct().
+            $this->_ns = sprintf('%s-%s', $this->getParam('namespace'), $params['user_id']);
+        }
+
+        // Check max DB string lengths.
+        if ((isset($params['storagetype']) && $params['storagetype'] == 'database') || $this->getParam('storagetype') == 'database') {
+            if (isset($params['user_id']) && mb_strlen($params['user_id']) > 32) {
+                $app->logMsg(sprintf('Prefs user_id param longer than 32 characters: %s', $params['user_id']), LOG_ERR, __FILE__, __LINE__);
+            }
+            if (isset($params['namespace']) && mb_strlen($params['namespace']) > 32) {
+                $app->logMsg(sprintf('Prefs namespace longer than 32 characters: %s', $this->_ns), LOG_ERR, __FILE__, __LINE__);
+            }
+            if (isset($params['pref_key']) && mb_strlen($params['pref_key']) > 64) {
+                $app->logMsg(sprintf('Prefs pref_key param longer than 64 characters: %s', $params['pref_key']), LOG_ERR, __FILE__, __LINE__);
+            }
+        }
+
         if (isset($params) && is_array($params)) {
             // Merge new parameters with old overriding only those passed.
@@ -213,4 +233,20 @@
             $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
             return null;
+        }
+    }
+
+    /*
+    * Setup the SESSION storage array. This method is called at the beginning of each method that accesses $_SESSION['_prefs'].
+    *
+    * @access   public
+    * @param
+    * @return
+    * @author   Quinn Comendant <quinn@strangecode.com>
+    * @since    02 Oct 2018 15:35:09
+    */
+    private function _init()
+    {
+        if ('cookie' != $this->getParam('storagetype') && !isset($_SESSION['_prefs'][$this->_ns])) {
+            $this->clear();
         }
     }
@@ -230,4 +266,6 @@
     {
         $app =& App::getInstance();
+
+        $this->_init();
 
         if (isset($defaults) && is_array($defaults)) {
@@ -275,4 +313,6 @@
             return false;
         }
+
+        $this->_init();
 
         switch ($this->getParam('storagetype')) {
@@ -317,4 +357,6 @@
         $app =& App::getInstance();
 
+        $this->_init();
+
         switch ($this->getParam('storagetype')) {
         case 'session':
@@ -352,4 +394,6 @@
     public function exists($key)
     {
+        $this->_init();
+
         switch ($this->getParam('storagetype')) {
         case 'session':
@@ -372,4 +416,6 @@
     {
         $app =& App::getInstance();
+
+        $this->_init();
 
         switch ($this->getParam('storagetype')) {
@@ -394,6 +440,7 @@
     /**
      * Resets all existing values under this namespace. This should be executed with the same consideration as $auth->clear(), such as when logging out.
-     */
-    public function clear($scope='all')
+     * Set $save true to persist the clear action to the storage (i.e., erase all stored prefs for this user).
+     */
+    public function clear($scope='all', $save=false)
     {
         $app =& App::getInstance();
@@ -431,5 +478,10 @@
         }
 
-        $app->logMsg(sprintf('Cleared %s %s prefs', $scope, $this->_ns), LOG_DEBUG, __FILE__, __LINE__);
+        if ($save) {
+            $this->save(true);
+            $app->logMsg(sprintf('Deleted all %s %s prefs for user_id %s', $this->getParam('storagetype'), $this->_ns, $this->getParam('user_id')), LOG_INFO, __FILE__, __LINE__);
+        } else {
+            $app->logMsg(sprintf('Cleared %s %s prefs', $scope, $this->_ns), LOG_DEBUG, __FILE__, __LINE__);
+        }
     }
 
@@ -457,4 +509,6 @@
         $this->initDB();
 
+        $this->_init();
+
         // Prefs already loaded for this session.
         if (!$force && $this->_isLoaded()) {
@@ -476,5 +530,5 @@
             FROM " . $db->escapeString($this->getParam('db_table')) . "
             WHERE user_id = '" . $db->escapeString($this->getParam('user_id')) . "'
-            AND pref_namespace = '" . $db->escapeString($this->_ns) . "'
+            AND pref_namespace = '" . $db->escapeString($this->getParam('namespace')) . "'
             LIMIT 10000
         ");
@@ -509,4 +563,6 @@
         }
 
+        $this->_init();
+
         if (isset($_SESSION['_prefs'][$this->_ns]['load_datetime'])
         && strtotime($_SESSION['_prefs'][$this->_ns]['load_datetime']) > time() - $this->getParam('load_timeout')
@@ -528,5 +584,5 @@
     * @since    04 Jun 2006 17:19:56
     */
-    public function save()
+    public function save($allow_empty=false)
     {
         $app =& App::getInstance();
@@ -547,10 +603,12 @@
         $this->initDB();
 
-        if (isset($_SESSION['_prefs'][$this->_ns]['saved']) && is_array($_SESSION['_prefs'][$this->_ns]['saved']) && !empty($_SESSION['_prefs'][$this->_ns]['saved'])) {
+        $this->_init();
+
+        if (isset($_SESSION['_prefs'][$this->_ns]['saved']) && is_array($_SESSION['_prefs'][$this->_ns]['saved']) && ($allow_empty || !empty($_SESSION['_prefs'][$this->_ns]['saved']))) {
             // 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) . "'
+                AND pref_namespace = '" . $db->escapeString($this->getParam('namespace')) . "'
             ");
 
@@ -560,17 +618,19 @@
                 $insert_values[] = sprintf("('%s', '%s', '%s', '%s')",
                     $db->escapeString($this->getParam('user_id')),
-                    $db->escapeString($this->_ns),
+                    $db->escapeString($this->getParam('namespace')),
                     $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__);
+            if (!empty($insert_values)) {
+                // 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 for user_id %s.', sizeof($insert_values), $this->getParam('user_id')), LOG_DEBUG, __FILE__, __LINE__);
+            }
+
             return true;
         }
@@ -597,4 +657,5 @@
         }
         // Use standardized class data names: _ + classname + namespace + variablekey
+        // (namespace = namespace + user_id)
         return sprintf('_prefs-%s-%s', $this->_ns, $key);
     }
Index: trunk/lib/Utilities.inc.php
===================================================================
--- trunk/lib/Utilities.inc.php	(revision 642)
+++ trunk/lib/Utilities.inc.php	(revision 644)
@@ -147,5 +147,5 @@
         $output .= sprintf("%s%s\n", $indent, $var);
     }
-    return $output;
+    return preg_replace(['/^[ \t]+$/', '/\n\n\n+/'], ['', "\n\n"], $output);
 }
 
