New Horde_Session API.
authorMichael M Slusarz <slusarz@curecanti.org>
Wed, 3 Nov 2010 06:46:56 +0000 (00:46 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Thu, 4 Nov 2010 19:39:33 +0000 (13:39 -0600)
framework/Core/lib/Horde/Session.php

index be7aff6..74233b1 100644 (file)
@@ -13,7 +13,7 @@
  * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
  * @package  Core
  */
-class Horde_Session implements ArrayAccess
+class Horde_Session
 {
     /* Class constants. */
     const DATA = '_d';
@@ -21,6 +21,9 @@ class Horde_Session implements ArrayAccess
     const PRUNE = '_p';
     const SERIALIZED = '_s';
 
+    const TYPE_ARRAY = 1;
+    const TYPE_OBJECT = 2;
+
     /**
      * Maximum size of the pruneable data store.
      *
@@ -167,133 +170,54 @@ class Horde_Session implements ArrayAccess
         $this->_cleansession = true;
     }
 
-    /**
-     * Return the list of subkeys for a master key.
-     *
-     * @param object $ob  See _parseOffset().
-     *
-     * @return array  Subkeyname (keys) and session variable name (values).
-     */
-    private function _subkeys($ob)
-    {
-        $ret = array();
-
-        if (isset($_SESSION[$ob->app]) &&
-            ($ob->name[strlen($ob->name) - 1] == '/')) {
-            foreach (array_keys($_SESSION[$ob->app]) as $k) {
-                if (strpos($k, $ob->name) === 0) {
-                    $ret[substr($k, strlen($ob->name))] = $k;
-                }
-            }
-        }
-
-        return $ret;
-    }
-
-    /* Session object storage. */
+    /* Session variable access. */
 
     /**
-     * Store an arbitrary piece of data in the session.
-     * Equivalent to: $this[self::DATA . ':' . $id] = $value;
+     * Does the session variable exist?
      *
-     * @param mixed $data     Data to save.
-     * @param boolean $prune  Is data pruneable?
-     * @param string $id      ID to use (otherwise, is autogenerated).
+     * @param string $app   Application name.
+     * @param string $name  Session variable name.
      *
-     * @return string  The session storage id (used to get session data).
+     * @return boolean  True if session variable exists.
      */
-    public function store($data, $prune = true, $id = null)
+    public function exists($app, $name)
     {
-        if (is_null($id)) {
-            $id = strval(new Horde_Support_Randomid());
-        } else {
-            $offset = $this->_parseOffset($id);
-            $id = $offset->name;
-        }
-
-        $full_id = self::DATA . ':' . $id;
-        $this->_offsetSet($this->_parseOffset($full_id), $data);
-
-        if ($prune) {
-            $ptr = &$_SESSION[self::PRUNE];
-            unset($ptr[$id]);
-            $ptr[$id] = 1;
-            if (count($ptr) > $this->maxStore) {
-                array_shift($ptr);
-            }
-        }
-
-        return $full_id;
+        return isset($_SESSION[$app][$name]);
     }
 
     /**
-     * Retrieve data from the session data store (created via store()).
-     * Equivalent to: $value = $this[self::DATA . ':' . $id];
-     *
-     * @param string $id  The session data ID.
+     * Get the value of a session variable.
      *
-     * @return mixed  The session data value.
-     */
-    public function retrieve($id)
-    {
-        $id = trim($id);
-
-        if (strpos($id, self::DATA) !== 0) {
-            $id = self::DATA . ':' . $id;
-        }
-
-        return $this[$id];
-    }
-
-    /**
-     * Purge data from the session data store (created via store()).
-     * Equivalent to: unset($this[self::DATA . ':' . $id]);
+     * @param string $app    Application name.
+     * @param string $name   Session variable name.
+     * @param integer $mask  One of:
+     * <pre>
+     * self::TYPE_ARRAY - Return an array value.
+     * self::TYPE_OBJECT - Return an object value.
+     * </pre>
      *
-     * @param string $id  The session data ID.
-     */
-    public function purge($id)
-    {
-        $id = trim($id);
-
-        if (strpos($id, self::DATA) !== 0) {
-            $id = self::DATA . ':' . $id;
-        }
-
-        unset($this[$id]);
-    }
-
-    /* ArrayAccess methods. */
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        $ob = $this->_parseOffset($offset);
-
-        return isset($_SESSION[$ob->app][$ob->name]);
-    }
-
-    /**
+     * @return mixed  The value or null if the value doesn't exist.
      */
-    public function offsetGet($offset)
+    public function get($app, $name, $mask = 0)
     {
-        $ob = $this->_parseOffset($offset);
-
-        if (!isset($_SESSION[$ob->app][$ob->name])) {
-            $subkeys = $this->_subkeys($ob);
-            if (!empty($subkeys)) {
+        if (!$this->exists($app, $name)) {
+            if ($subkeys = $this->_subkeys($app, $name)) {
                 $ret = array();
                 foreach ($subkeys as $k => $v) {
-                    $ret[$k] = $this[$v];
+                    $ret[$k] = $this->get($app, $v, $mask);
                 }
                 return $ret;
             }
 
-            switch ($ob->type) {
-            case 'array':
+            if (strpos($name, self::DATA) === 0) {
+                return $this->retrieve($name);
+            }
+
+            switch ($mask) {
+            case self::TYPE_ARRAY:
                 return array();
 
-            case 'object':
+            case self::TYPE_OBJECT:
                 return new stdClass;
 
             default:
@@ -301,137 +225,200 @@ class Horde_Session implements ArrayAccess
             }
         }
 
-        $data = $_SESSION[$ob->app][$ob->name];
+        $data = $_SESSION[$app][$name];
+        $key = $this->_getKey($app, $name);
 
-        if (!isset($_SESSION[self::SERIALIZED][$ob->key])) {
+        if (!isset($_SESSION[self::SERIALIZED][$key])) {
             return $data;
         }
 
         if ($this->_lzf &&
             (($data = @lzf_decompress($data)) === false)) {
-            unset($this[$offset]);
-            return $this[$offset];
+            $this->remove($app, $name);
+            return $this->get($app, $name);
         }
 
-        return ($_SESSION[self::SERIALIZED][$ob->key] == 's')
+        return ($_SESSION[self::SERIALIZED][$key] == 's')
             ? @unserialize($data)
             : json_decode($data, true);
     }
 
     /**
+     * Sets the value of a session variable.
+     *
+     * @param string $app    Application name.
+     * @param string $name   Session variable name.
+     * @param mixed $value   Session variable value.
+     * <pre>
+     * self::TYPE_ARRAY - Force save as an array value.
+     * self::TYPE_OBJECT - Force save as an object value.
+     * </pre>
      */
-    public function offsetSet($offset, $value)
+    public function set($app, $name, $value, $mask = 0)
     {
-        $ob = $this->_parseOffset($offset);
-
-        if ($ob->app == self::DATA) {
-            $this->store($value, false, $ob->name);
-        } else {
-            $this->_offsetSet($ob, $value);
-        }
-    }
+        $key = $this->_getKey($app, $name);
 
-    /**
-     * TODO
-     */
-    private function _offsetSet($ob, $value)
-    {
         /* Each particular piece of session data is generally not used on any
          * given page load.  Thus, for arrays and objects, it is beneficial to
          * always convert to string representations so that the object/array
          * does not need to be rebuilt every time the session is reloaded. */
-        if (is_object($value) || ($ob->type == 'object')) {
+        if (is_object($value) || ($mask & self::TYPE_OBJECT)) {
             $value = serialize($value);
             if ($this->_lzf) {
                 $value = lzf_compress($value);
             }
-            $_SESSION[self::SERIALIZED][$ob->key] = 's';
-        } elseif (is_array($value) || ($ob->type == 'array')) {
+            $_SESSION[self::SERIALIZED][$key] = 's';
+        } elseif (is_array($value) || ($mask & self::TYPE_ARRAY)) {
             $value = json_encode($value);
             if ($this->_lzf) {
                 $value = lzf_compress($value);
             }
-            $_SESSION[self::SERIALIZED][$ob->key] = 'j';
+            $_SESSION[self::SERIALIZED][$key] = 'j';
         } else {
-            unset($_SESSION[self::SERIALIZED][$ob->key]);
+            unset($_SESSION[self::SERIALIZED][$key]);
         }
 
-        $_SESSION[$ob->app][$ob->name] = $value;
+        $_SESSION[$app][$name] = $value;
         $this->sessionHandler->changed = true;
     }
 
     /**
+     * Remove session key(s).
+     *
+     * @param string $app    Application name.
+     * @param string $name   Session variable name.
      */
-    public function offsetUnset($offset)
+    public function remove($app, $name = null)
     {
-        $ob = $this->_parseOffset($offset);
+        if (!isset($_SESSION[$app])) {
+            return;
+        }
 
-        if (isset($_SESSION[$ob->app])) {
-            if (!strlen($ob->name)) {
-                foreach (array_keys($_SESSION[$ob->app]) as $key) {
-                    unset($_SESSION[self::SERIALIZED][$key]);
-                }
-                unset($_SESSION[$ob->app]);
-            } elseif (isset($_SESSION[$ob->app][$ob->name])) {
-                unset(
-                    $_SESSION[$ob->app][$ob->name],
-                    $_SESSION[self::PRUNE][$ob->key],
-                    $_SESSION[self::SERIALIZED][$ob->key]
-                );
-            } else {
-                foreach ($this->_subkeys($ob) as $val) {
-                    unset($this[$val]);
+        if (is_null($name)) {
+            foreach (array_keys($_SESSION[$app]) as $key) {
+                unset($_SESSION[self::SERIALIZED][$key]);
+            }
+            unset($_SESSION[$app]);
+        } elseif (isset($_SESSION[$app][$name])) {
+            $key = $this->_getKey($app, $name);
+            unset(
+                $_SESSION[$app][$name],
+                $_SESSION[self::PRUNE][$key],
+                $_SESSION[self::SERIALIZED][$key]
+            );
+        } else {
+            foreach ($this->_subkeys($app, $name) as $val) {
+                $this->remove($app, $val);
+            }
+        }
+    }
+
+    /**
+     * Generates the unique storage key.
+     *
+     * @param string $app    Application name.
+     * @param string $name   Session variable name.
+     *
+     * @return string  The unique storage key.
+     */
+    private function _getKey($app, $name)
+    {
+        return $app . ':' . $name;
+    }
+
+    /**
+     * Return the list of subkeys for a master key.
+     *
+     * @param string $app    Application name.
+     * @param string $name   Session variable name.
+     *
+     * @return array  Subkeyname (keys) and session variable name (values).
+     */
+    private function _subkeys($app, $name)
+    {
+        $ret = array();
+
+        if (isset($_SESSION[$app]) &&
+            ($name[strlen($name) - 1] == '/')) {
+            foreach (array_keys($_SESSION[$app]) as $k) {
+                if (strpos($k, $name) === 0) {
+                    $ret[substr($k, strlen($name))] = $k;
                 }
             }
         }
+
+        return $ret;
     }
 
-    /* ArrayAccess helper methods. */
+    /* Session object storage. */
 
     /**
-     * Parses a session variable identifier.
-     * Format:
-     * <pre>
-     * [app:]name[/subkey][;default]
+     * Store an arbitrary piece of data in the session.
      *
-     * app - Application name.
-     *       DEFAULT: horde
-     * default - Default value type to return if value doesn't exist.
-     *           Valid types: array, object
-     *           DEFAULT: none
-     * subkey - Indicate that this entry is a subkey of the master name key.
-     *          Requesting a session key with a trailing '/' will retrieve all
-     *          subkeys of the given master key.
-     * </pre>
+     * @param mixed $data     Data to save.
+     * @param boolean $prune  Is data pruneable?
+     * @param string $id      ID to use (otherwise, is autogenerated).
      *
-     * @return object  Object with the following properties:
-     * <pre>
-     * app - Application name.
-     * key - Offset key.
-     * name - Variable name.
-     * type - Variable type.
-     * </pre>
+     * @return string  The session storage id (used to retrieve session data).
      */
-    private function _parseOffset($offset)
+    public function store($data, $prune = true, $id = null)
     {
-        $ob = new stdClass;
+        $id = is_null($id)
+            ? strval(new Horde_Support_Randomid())
+            : $this->_getStoreId($id);
 
-        $parts = explode(':', $offset);
-        if (isset($parts[1])) {
-            $ob->app = $parts[0];
-            $type = explode(';', $parts[1]);
-        } else {
-            $ob->app = 'horde';
-            $type = explode(';', $parts[0]);
+        $this->set(self::DATA, $id, $data);
+
+        if ($prune) {
+            $ptr = &$_SESSION[self::PRUNE];
+            unset($ptr[$id]);
+            $ptr[$id] = 1;
+            if (count($ptr) > $this->maxStore) {
+                array_shift($ptr);
+            }
         }
 
-        $ob->name = $type[0];
-        $ob->key = $ob->app . ':' . $ob->name;
-        $ob->type = isset($type[1])
-            ? $type[1]
-            : 'scalar';
+        return $this->_getKey(self::DATA, $id);
+    }
+
+    /**
+     * Retrieve data from the session data store (created via store()).
+     *
+     * @param string $id  The session data ID.
+     *
+     * @return mixed  The session data value.
+     */
+    public function retrieve($id)
+    {
+        return $this->get(self::DATA, $this->_getStoreId($id));
+    }
+
+    /**
+     * Purge data from the session data store (created via store()).
+     *
+     * @param string $id  The session data ID.
+     */
+    public function purge($id)
+    {
+        $this->remove(self::DATA, $this->_getStoreId($id));
+    }
+
+    /**
+     * Returns the base storage ID.
+     *
+     * @param string $id  The session data ID.
+     *
+     * @return string  The base storage ID (without prefix).
+     */
+    private function _getStoreId($id)
+    {
+        $id = trim($id);
+
+        if (strpos($id, self::DATA) === 0) {
+            $id = substr($id, strlen(self::DATA) + 1);
+        }
 
-        return $ob;
+        return $id;
     }
 
 }