* @license http://www.fsf.org/copyleft/lgpl.html LGPL
* @package Core
*/
-class Horde_Session implements ArrayAccess
+class Horde_Session
{
/* Class constants. */
const DATA = '_d';
const PRUNE = '_p';
const SERIALIZED = '_s';
+ const TYPE_ARRAY = 1;
+ const TYPE_OBJECT = 2;
+
/**
* Maximum size of the pruneable data store.
*
$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:
}
}
- $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;
}
}