1: <?php
2: /**
3: * CHttpSession class file.
4: *
5: * @author Qiang Xue <qiang.xue@gmail.com>
6: * @link http://www.yiiframework.com/
7: * @copyright 2008-2013 Yii Software LLC
8: * @license http://www.yiiframework.com/license/
9: */
10:
11: /**
12: * CHttpSession provides session-level data management and the related configurations.
13: *
14: * To start the session, call {@link open()}; To complete and send out session data, call {@link close()};
15: * To destroy the session, call {@link destroy()}.
16: *
17: * If {@link autoStart} is set true, the session will be started automatically
18: * when the application component is initialized by the application.
19: *
20: * CHttpSession can be used like an array to set and get session data. For example,
21: * <pre>
22: * $session=new CHttpSession;
23: * $session->open();
24: * $value1=$session['name1']; // get session variable 'name1'
25: * $value2=$session['name2']; // get session variable 'name2'
26: * foreach($session as $name=>$value) // traverse all session variables
27: * $session['name3']=$value3; // set session variable 'name3'
28: * </pre>
29: *
30: * The following configurations are available for session:
31: * <ul>
32: * <li>{@link setSessionID sessionID};</li>
33: * <li>{@link setSessionName sessionName};</li>
34: * <li>{@link autoStart};</li>
35: * <li>{@link setSavePath savePath};</li>
36: * <li>{@link setCookieParams cookieParams};</li>
37: * <li>{@link setGCProbability gcProbability};</li>
38: * <li>{@link setCookieMode cookieMode};</li>
39: * <li>{@link setUseTransparentSessionID useTransparentSessionID};</li>
40: * <li>{@link setTimeout timeout}.</li>
41: * </ul>
42: * See the corresponding setter and getter documentation for more information.
43: * Note, these properties must be set before the session is started.
44: *
45: * CHttpSession can be extended to support customized session storage.
46: * Override {@link openSession}, {@link closeSession}, {@link readSession},
47: * {@link writeSession}, {@link destroySession} and {@link gcSession}
48: * and set {@link useCustomStorage} to true.
49: * Then, the session data will be stored and retrieved using the above methods.
50: *
51: * CHttpSession is a Web application component that can be accessed via
52: * {@link CWebApplication::getSession()}.
53: *
54: * @property boolean $useCustomStorage Whether to use custom storage.
55: * @property boolean $isStarted Whether the session has started.
56: * @property string $sessionID The current session ID.
57: * @property string $sessionName The current session name.
58: * @property string $savePath The current session save path, defaults to {@link http://php.net/session.save_path}.
59: * @property array $cookieParams The session cookie parameters.
60: * @property string $cookieMode How to use cookie to store session ID. Defaults to 'Allow'.
61: * @property float $gCProbability The probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
62: * @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to false.
63: * @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds.
64: * @property CHttpSessionIterator $iterator An iterator for traversing the session variables.
65: * @property integer $count The number of session variables.
66: * @property array $keys The list of session variable names.
67: *
68: * @author Qiang Xue <qiang.xue@gmail.com>
69: * @package system.web
70: * @since 1.0
71: */
72: class CHttpSession extends CApplicationComponent implements IteratorAggregate,ArrayAccess,Countable
73: {
74: /**
75: * @var boolean whether the session should be automatically started when the session application component is initialized, defaults to true.
76: */
77: public $autoStart=true;
78:
79: /**
80: * Initializes the application component.
81: * This method is required by IApplicationComponent and is invoked by application.
82: */
83: public function init()
84: {
85: parent::init();
86:
87: if($this->autoStart)
88: $this->open();
89: register_shutdown_function(array($this,'close'));
90: }
91:
92: /**
93: * Returns a value indicating whether to use custom session storage.
94: * This method should be overriden to return true if custom session storage handler should be used.
95: * If returning true, make sure the methods {@link openSession}, {@link closeSession}, {@link readSession},
96: * {@link writeSession}, {@link destroySession}, and {@link gcSession} are overridden in child
97: * class, because they will be used as the callback handlers.
98: * The default implementation always return false.
99: * @return boolean whether to use custom storage.
100: */
101: public function getUseCustomStorage()
102: {
103: return false;
104: }
105:
106: /**
107: * Starts the session if it has not started yet.
108: */
109: public function open()
110: {
111: if($this->getUseCustomStorage())
112: @session_set_save_handler(array($this,'openSession'),array($this,'closeSession'),array($this,'readSession'),array($this,'writeSession'),array($this,'destroySession'),array($this,'gcSession'));
113:
114: @session_start();
115: if(YII_DEBUG && session_id()=='')
116: {
117: $message=Yii::t('yii','Failed to start session.');
118: if(function_exists('error_get_last'))
119: {
120: $error=error_get_last();
121: if(isset($error['message']))
122: $message=$error['message'];
123: }
124: Yii::log($message, CLogger::LEVEL_WARNING, 'system.web.CHttpSession');
125: }
126: }
127:
128: /**
129: * Ends the current session and store session data.
130: */
131: public function close()
132: {
133: if(session_id()!=='')
134: @session_write_close();
135: }
136:
137: /**
138: * Frees all session variables and destroys all data registered to a session.
139: */
140: public function destroy()
141: {
142: if(session_id()!=='')
143: {
144: @session_unset();
145: @session_destroy();
146: }
147: }
148:
149: /**
150: * @return boolean whether the session has started
151: */
152: public function getIsStarted()
153: {
154: return session_id()!=='';
155: }
156:
157: /**
158: * @return string the current session ID
159: */
160: public function getSessionID()
161: {
162: return session_id();
163: }
164:
165: /**
166: * @param string $value the session ID for the current session
167: */
168: public function setSessionID($value)
169: {
170: session_id($value);
171: }
172:
173: /**
174: * Updates the current session id with a newly generated one .
175: * Please refer to {@link http://php.net/session_regenerate_id} for more details.
176: * @param boolean $deleteOldSession Whether to delete the old associated session file or not.
177: * @since 1.1.8
178: */
179: public function regenerateID($deleteOldSession=false)
180: {
181: if($this->getIsStarted())
182: session_regenerate_id($deleteOldSession);
183: }
184:
185: /**
186: * @return string the current session name
187: */
188: public function getSessionName()
189: {
190: return session_name();
191: }
192:
193: /**
194: * @param string $value the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID
195: */
196: public function setSessionName($value)
197: {
198: session_name($value);
199: }
200:
201: /**
202: * @return string the current session save path, defaults to {@link http://php.net/session.save_path}.
203: */
204: public function getSavePath()
205: {
206: return session_save_path();
207: }
208:
209: /**
210: * @param string $value the current session save path
211: * @throws CException if the path is not a valid directory
212: */
213: public function setSavePath($value)
214: {
215: if(is_dir($value))
216: session_save_path($value);
217: else
218: throw new CException(Yii::t('yii','CHttpSession.savePath "{path}" is not a valid directory.',
219: array('{path}'=>$value)));
220: }
221:
222: /**
223: * @return array the session cookie parameters.
224: * @see http://us2.php.net/manual/en/function.session-get-cookie-params.php
225: */
226: public function getCookieParams()
227: {
228: return session_get_cookie_params();
229: }
230:
231: /**
232: * Sets the session cookie parameters.
233: * The effect of this method only lasts for the duration of the script.
234: * Call this method before the session starts.
235: * @param array $value cookie parameters, valid keys include: lifetime, path,
236: * domain, secure, httponly. Note that httponly is all lowercase.
237: * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
238: */
239: public function setCookieParams($value)
240: {
241: $data=session_get_cookie_params();
242: extract($data);
243: extract($value);
244: if(isset($httponly))
245: session_set_cookie_params($lifetime,$path,$domain,$secure,$httponly);
246: else
247: session_set_cookie_params($lifetime,$path,$domain,$secure);
248: }
249:
250: /**
251: * @return string how to use cookie to store session ID. Defaults to 'Allow'.
252: */
253: public function getCookieMode()
254: {
255: if(ini_get('session.use_cookies')==='0')
256: return 'none';
257: elseif(ini_get('session.use_only_cookies')==='0')
258: return 'allow';
259: else
260: return 'only';
261: }
262:
263: /**
264: * @param string $value how to use cookie to store session ID. Valid values include 'none', 'allow' and 'only'.
265: */
266: public function setCookieMode($value)
267: {
268: if($value==='none')
269: {
270: ini_set('session.use_cookies','0');
271: ini_set('session.use_only_cookies','0');
272: }
273: elseif($value==='allow')
274: {
275: ini_set('session.use_cookies','1');
276: ini_set('session.use_only_cookies','0');
277: }
278: elseif($value==='only')
279: {
280: ini_set('session.use_cookies','1');
281: ini_set('session.use_only_cookies','1');
282: }
283: else
284: throw new CException(Yii::t('yii','CHttpSession.cookieMode can only be "none", "allow" or "only".'));
285: }
286:
287: /**
288: * @return float the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
289: */
290: public function getGCProbability()
291: {
292: return (float)(ini_get('session.gc_probability')/ini_get('session.gc_divisor')*100);
293: }
294:
295: /**
296: * @param float $value the probability (percentage) that the gc (garbage collection) process is started on every session initialization.
297: * @throws CException if the value is beyond [0,100]
298: */
299: public function setGCProbability($value)
300: {
301: if($value>=0 && $value<=100)
302: {
303: // percent * 21474837 / 2147483647 ≈ percent * 0.01
304: ini_set('session.gc_probability',floor($value*21474836.47));
305: ini_set('session.gc_divisor',2147483647);
306: }
307: else
308: throw new CException(Yii::t('yii','CHttpSession.gcProbability "{value}" is invalid. It must be a float between 0 and 100.',
309: array('{value}'=>$value)));
310: }
311:
312: /**
313: * @return boolean whether transparent sid support is enabled or not, defaults to false.
314: */
315: public function getUseTransparentSessionID()
316: {
317: return ini_get('session.use_trans_sid')==1;
318: }
319:
320: /**
321: * @param boolean $value whether transparent sid support is enabled or not.
322: */
323: public function setUseTransparentSessionID($value)
324: {
325: ini_set('session.use_trans_sid',$value?'1':'0');
326: }
327:
328: /**
329: * @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds.
330: */
331: public function getTimeout()
332: {
333: return (int)ini_get('session.gc_maxlifetime');
334: }
335:
336: /**
337: * @param integer $value the number of seconds after which data will be seen as 'garbage' and cleaned up
338: */
339: public function setTimeout($value)
340: {
341: ini_set('session.gc_maxlifetime',$value);
342: }
343:
344: /**
345: * Session open handler.
346: * This method should be overridden if {@link useCustomStorage} is set true.
347: * Do not call this method directly.
348: * @param string $savePath session save path
349: * @param string $sessionName session name
350: * @return boolean whether session is opened successfully
351: */
352: public function openSession($savePath,$sessionName)
353: {
354: return true;
355: }
356:
357: /**
358: * Session close handler.
359: * This method should be overridden if {@link useCustomStorage} is set true.
360: * Do not call this method directly.
361: * @return boolean whether session is closed successfully
362: */
363: public function closeSession()
364: {
365: return true;
366: }
367:
368: /**
369: * Session read handler.
370: * This method should be overridden if {@link useCustomStorage} is set true.
371: * Do not call this method directly.
372: * @param string $id session ID
373: * @return string the session data
374: */
375: public function readSession($id)
376: {
377: return '';
378: }
379:
380: /**
381: * Session write handler.
382: * This method should be overridden if {@link useCustomStorage} is set true.
383: * Do not call this method directly.
384: * @param string $id session ID
385: * @param string $data session data
386: * @return boolean whether session write is successful
387: */
388: public function writeSession($id,$data)
389: {
390: return true;
391: }
392:
393: /**
394: * Session destroy handler.
395: * This method should be overridden if {@link useCustomStorage} is set true.
396: * Do not call this method directly.
397: * @param string $id session ID
398: * @return boolean whether session is destroyed successfully
399: */
400: public function destroySession($id)
401: {
402: return true;
403: }
404:
405: /**
406: * Session GC (garbage collection) handler.
407: * This method should be overridden if {@link useCustomStorage} is set true.
408: * Do not call this method directly.
409: * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
410: * @return boolean whether session is GCed successfully
411: */
412: public function gcSession($maxLifetime)
413: {
414: return true;
415: }
416:
417: //------ The following methods enable CHttpSession to be CMap-like -----
418:
419: /**
420: * Returns an iterator for traversing the session variables.
421: * This method is required by the interface IteratorAggregate.
422: * @return CHttpSessionIterator an iterator for traversing the session variables.
423: */
424: public function getIterator()
425: {
426: return new CHttpSessionIterator;
427: }
428:
429: /**
430: * Returns the number of items in the session.
431: * @return integer the number of session variables
432: */
433: public function getCount()
434: {
435: return count($_SESSION);
436: }
437:
438: /**
439: * Returns the number of items in the session.
440: * This method is required by Countable interface.
441: * @return integer number of items in the session.
442: */
443: public function count()
444: {
445: return $this->getCount();
446: }
447:
448: /**
449: * @return array the list of session variable names
450: */
451: public function getKeys()
452: {
453: return array_keys($_SESSION);
454: }
455:
456: /**
457: * Returns the session variable value with the session variable name.
458: * This method is very similar to {@link itemAt} and {@link offsetGet},
459: * except that it will return $defaultValue if the session variable does not exist.
460: * @param mixed $key the session variable name
461: * @param mixed $defaultValue the default value to be returned when the session variable does not exist.
462: * @return mixed the session variable value, or $defaultValue if the session variable does not exist.
463: * @since 1.1.2
464: */
465: public function get($key,$defaultValue=null)
466: {
467: return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
468: }
469:
470: /**
471: * Returns the session variable value with the session variable name.
472: * This method is exactly the same as {@link offsetGet}.
473: * @param mixed $key the session variable name
474: * @return mixed the session variable value, null if no such variable exists
475: */
476: public function itemAt($key)
477: {
478: return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
479: }
480:
481: /**
482: * Adds a session variable.
483: * Note, if the specified name already exists, the old value will be removed first.
484: * @param mixed $key session variable name
485: * @param mixed $value session variable value
486: */
487: public function add($key,$value)
488: {
489: $_SESSION[$key]=$value;
490: }
491:
492: /**
493: * Removes a session variable.
494: * @param mixed $key the name of the session variable to be removed
495: * @return mixed the removed value, null if no such session variable.
496: */
497: public function remove($key)
498: {
499: if(isset($_SESSION[$key]))
500: {
501: $value=$_SESSION[$key];
502: unset($_SESSION[$key]);
503: return $value;
504: }
505: else
506: return null;
507: }
508:
509: /**
510: * Removes all session variables
511: */
512: public function clear()
513: {
514: foreach(array_keys($_SESSION) as $key)
515: unset($_SESSION[$key]);
516: }
517:
518: /**
519: * @param mixed $key session variable name
520: * @return boolean whether there is the named session variable
521: */
522: public function contains($key)
523: {
524: return isset($_SESSION[$key]);
525: }
526:
527: /**
528: * @return array the list of all session variables in array
529: */
530: public function toArray()
531: {
532: return $_SESSION;
533: }
534:
535: /**
536: * This method is required by the interface ArrayAccess.
537: * @param mixed $offset the offset to check on
538: * @return boolean
539: */
540: public function offsetExists($offset)
541: {
542: return isset($_SESSION[$offset]);
543: }
544:
545: /**
546: * This method is required by the interface ArrayAccess.
547: * @param integer $offset the offset to retrieve element.
548: * @return mixed the element at the offset, null if no element is found at the offset
549: */
550: public function offsetGet($offset)
551: {
552: return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
553: }
554:
555: /**
556: * This method is required by the interface ArrayAccess.
557: * @param integer $offset the offset to set element
558: * @param mixed $item the element value
559: */
560: public function offsetSet($offset,$item)
561: {
562: $_SESSION[$offset]=$item;
563: }
564:
565: /**
566: * This method is required by the interface ArrayAccess.
567: * @param mixed $offset the offset to unset element
568: */
569: public function offsetUnset($offset)
570: {
571: unset($_SESSION[$offset]);
572: }
573: }
574: