1: <?php
2: /**
3: * The Horde_Session_Null:: class provides a set of methods for handling the
4: * administration and contents of the Horde session variable when the PHP
5: * session is not desired. Needed so things like application authentication can
6: * work within a single HTTP request when we don't need the overhead of a
7: * full PHP session.
8: *
9: * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
10: *
11: * See the enclosed file COPYING for license information (LGPL). If you
12: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
13: *
14: * @author Michael Slusarz <slusarz@horde.org>
15: * @category Horde
16: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
17: * @package Core
18: */
19: class Horde_Session_Null extends Horde_Session
20: {
21: protected $_data = array();
22:
23: /**
24: * Constructor.
25: */
26: public function __construct()
27: {
28: // We must start a session to ensure that session_id() is available.
29: // Places such as Horde_Secret use it as an encryption key.
30: session_start();
31: }
32:
33: /**
34: * Sets a custom session handler up, if there is one.
35: *
36: * @param boolean $start Initiate the session?
37: * @param string $cache_limiter Override for the session cache limiter
38: * value.
39: * @param string $session_id The session ID to use.
40: *
41: * @throws Horde_Exception
42: */
43: public function setup($start = true, $cache_limiter = null,
44: $session_id = null)
45: {
46: }
47:
48: /**
49: * Starts the session.
50: *
51: * @since 1.4.0
52: */
53: public function start()
54: {
55: }
56:
57: /**
58: * Destroys any existing session on login and make sure to use a new
59: * session ID, to avoid session fixation issues. Should be called before
60: * checking a login.
61: *
62: * @return boolean True if the session was cleaned.
63: */
64: public function clean()
65: {
66: $this->_data = array();
67: return true;
68: }
69:
70: /**
71: * Close the current session.
72: */
73: public function close()
74: {
75: session_destroy();
76: }
77:
78: /**
79: * Destroy session data.
80: */
81: public function destroy()
82: {
83: $this->clean();
84: session_destroy();
85: }
86:
87: /**
88: * Is the current session active (read/write)?
89: *
90: * @return boolean True if the current session is active.
91: */
92: public function isActive()
93: {
94: return true;
95: }
96:
97: /* Session variable access. */
98:
99: /**
100: * Does the session variable exist?
101: *
102: * @param string $app Application name.
103: * @param string $name Session variable name.
104: *
105: * @return boolean True if session variable exists.
106: */
107: public function exists($app, $name)
108: {
109: return isset($this->_data[$app][self::NOT_SERIALIZED . $name]) ||
110: isset($this->_data[$app][self::IS_SERIALIZED . $name]);
111: }
112:
113: /**
114: * Get the value of a session variable.
115: *
116: * @param string $app Application name.
117: * @param string $name Session variable name.
118: * @param integer $mask One of:
119: * - self::TYPE_ARRAY - Return an array value.
120: * - self::TYPE_OBJECT - Return an object value.
121: *
122: * @return mixed The value or null if the value doesn't exist.
123: */
124: public function get($app, $name, $mask = 0)
125: {
126: if (isset($this->_data[$app][self::NOT_SERIALIZED . $name])) {
127: return $this->_data[$app][self::NOT_SERIALIZED . $name];
128: } elseif (isset($this->_data[$app][self::IS_SERIALIZED . $name])) {
129: $data = $this->_data[$app][self::IS_SERIALIZED . $name];
130: return @unserialize($data);
131: }
132:
133: if ($subkeys = $this->_subkeys($app, $name)) {
134: $ret = array();
135: foreach ($subkeys as $k => $v) {
136: $ret[$k] = $this->get($app, $v, $mask);
137: }
138: return $ret;
139: }
140:
141: if (strpos($name, self::DATA) === 0) {
142: return $this->retrieve($name);
143: }
144:
145: switch ($mask) {
146: case self::TYPE_ARRAY:
147: return array();
148:
149: case self::TYPE_OBJECT:
150: return new stdClass;
151: }
152:
153: return null;
154: }
155:
156: /**
157: * Sets the value of a session variable.
158: *
159: * @param string $app Application name.
160: * @param string $name Session variable name.
161: * @param mixed $value Session variable value.
162: * @param integer $mask One of:
163: * - self::TYPE_ARRAY - Force save as an array value.
164: * - self::TYPE_OBJECT - Force save as an object value.
165: */
166: public function set($app, $name, $value, $mask = 0)
167: {
168:
169: /* Each particular piece of session data is generally not used on any
170: * given page load. Thus, for arrays and objects, it is beneficial to
171: * always convert to string representations so that the object/array
172: * does not need to be rebuilt every time the session is reloaded. */
173: if (is_object($value) || ($mask & self::TYPE_OBJECT) ||
174: is_array($value) || ($mask & self::TYPE_ARRAY)) {
175: $this->_data[$app][self::IS_SERIALIZED . $name] = serialize($value);
176: unset($this->_data[$app][self::NOT_SERIALIZED . $name]);
177: } else {
178: $this->_data[$app][self::NOT_SERIALIZED . $name] = $value;
179: unset($this->_data[$app][self::IS_SERIALIZED . $name]);
180: }
181: }
182:
183: /**
184: * Remove session key(s).
185: *
186: * @param string $app Application name.
187: * @param string $name Session variable name.
188: */
189: public function remove($app, $name = null)
190: {
191: if (!isset($this->_data[$app])) {
192: return;
193: }
194:
195: if (is_null($name)) {
196: unset($this->_data[$app]);
197: } elseif ($this->exists($app, $name)) {
198: unset(
199: $this->_data[$app][self::NOT_SERIALIZED . $name],
200: $this->_data[$app][self::IS_SERIALIZED . $name],
201: $this->_data[self::PRUNE][$this->_getKey($app, $name)]
202: );
203: } else {
204: foreach ($this->_subkeys($app, $name) as $val) {
205: $this->remove($app, $val);
206: }
207: }
208: }
209:
210: /**
211: * Generates the unique storage key.
212: *
213: * @param string $app Application name.
214: * @param string $name Session variable name.
215: *
216: * @return string The unique storage key.
217: */
218: private function _getKey($app, $name)
219: {
220: return $app . ':' . $name;
221: }
222:
223: /**
224: * Return the list of subkeys for a master key.
225: *
226: * @param string $app Application name.
227: * @param string $name Session variable name.
228: *
229: * @return array Subkeyname (keys) and session variable name (values).
230: */
231: private function _subkeys($app, $name)
232: {
233: $ret = array();
234:
235: if ($name &&
236: isset($this->_data[$app]) &&
237: ($name[strlen($name) - 1] == '/')) {
238: foreach (array_keys($this->_data[$app]) as $k) {
239: if (strpos($k, $name) === 1) {
240: $ret[substr($k, strlen($name) + 1)] = substr($k, 1);
241: }
242: }
243: }
244:
245: return $ret;
246: }
247:
248: /* Session object storage. */
249:
250: /**
251: * Store an arbitrary piece of data in the session.
252: *
253: * @param mixed $data Data to save.
254: * @param boolean $prune Is data pruneable?
255: * @param string $id ID to use (otherwise, is autogenerated).
256: *
257: * @return string The session storage id (used to retrieve session data).
258: */
259: public function store($data, $prune = true, $id = null)
260: {
261: $id = is_null($id)
262: ? strval(new Horde_Support_Randomid())
263: : $this->_getStoreId($id);
264:
265: $this->set(self::DATA, $id, $data);
266:
267: if ($prune) {
268: $ptr = &$this->_data[self::PRUNE];
269: unset($ptr[$id]);
270: $ptr[$id] = 1;
271: if (count($ptr) > $this->maxStore) {
272: array_shift($ptr);
273: }
274: }
275:
276: return $this->_getKey(self::DATA, $id);
277: }
278:
279: /**
280: * Retrieve data from the session data store (created via store()).
281: *
282: * @param string $id The session data ID.
283: *
284: * @return mixed The session data value.
285: */
286: public function retrieve($id)
287: {
288: return $this->get(self::DATA, $this->_getStoreId($id));
289: }
290:
291: /**
292: * Purge data from the session data store (created via store()).
293: *
294: * @param string $id The session data ID.
295: */
296: public function purge($id)
297: {
298: $this->remove(self::DATA, $this->_getStoreId($id));
299: }
300:
301: /**
302: * Returns the base storage ID.
303: *
304: * @param string $id The session data ID.
305: *
306: * @return string The base storage ID (without prefix).
307: */
308: private function _getStoreId($id)
309: {
310: $id = trim($id);
311:
312: if (strpos($id, self::DATA) === 0) {
313: $id = substr($id, strlen(self::DATA) + 1);
314: }
315:
316: return $id;
317: }
318:
319: }
320: