3
* Session class for Cake.
5
* Cake abstracts the handling of sessions.
6
* There are several convenient methods to access session information.
7
* This class is the implementation of those methods.
8
* They are mostly used by the Session Component.
10
* PHP versions 4 and 5
12
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
13
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
15
* Licensed under The MIT License
16
* Redistributions of files must retain the above copyright notice.
18
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
19
* @link http://cakephp.org CakePHP(tm) Project
21
* @subpackage cake.cake.libs
22
* @since CakePHP(tm) v .0.10.0.1222
23
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
27
* Session class for Cake.
29
* Cake abstracts the handling of sessions. There are several convenient methods to access session information.
30
* This class is the implementation of those methods. They are mostly used by the Session Component.
33
* @subpackage cake.cake.libs
35
class CakeSession extends Object {
38
* True if the Session is still valid
46
* Error messages for this session
62
* Path to where the session is active.
70
* Error number of last occurred error
75
var $lastError = null;
78
* 'Security.level' setting, "high", "medium", or "low".
86
* Start time for this session.
94
* Time when this session becomes invalid.
99
var $sessionTime = false;
102
* The number of seconds to set for session.cookie_lifetime. 0 means
107
var $cookieLifeTime = false;
110
* Keeps track of keys to watch for writes on
115
var $watchKeys = array();
134
* Session timeout multiplier factor
144
* @param string $base The base path for the Session
145
* @param boolean $start Should session be started right now
148
function __construct($base = null, $start = true) {
149
App::import('Core', array('Set', 'Security'));
150
$this->time = time();
152
if (Configure::read('Session.checkAgent') === true || Configure::read('Session.checkAgent') === null) {
153
if (env('HTTP_USER_AGENT') != null) {
154
$this->_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
157
if (Configure::read('Session.save') === 'database') {
158
$modelName = Configure::read('Session.model');
159
$database = Configure::read('Session.database');
160
$table = Configure::read('Session.table');
162
if (empty($database)) {
163
$database = 'default';
166
'class' => 'Session',
167
'alias' => 'Session',
168
'table' => 'cake_sessions',
171
if (!empty($modelName)) {
172
$settings['class'] = $modelName;
174
if (!empty($table)) {
175
$settings['table'] = $table;
177
ClassRegistry::init($settings);
179
if ($start === true) {
182
if (strpos($base, 'index.php') !== false) {
183
$this->path = str_replace('index.php', '', $base);
185
if (strpos($base, '?') !== false) {
186
$this->path = str_replace('?', '', $base);
189
$this->host = env('HTTP_HOST');
191
if (strpos($this->host, ':') !== false) {
192
$this->host = substr($this->host, 0, strpos($this->host, ':'));
195
if (isset($_SESSION) || $start === true) {
196
if (!class_exists('Security')) {
197
App::import('Core', 'Security');
199
$this->sessionTime = $this->time + (Security::inactiveMins() * Configure::read('Session.timeout'));
200
$this->security = Configure::read('Security.level');
202
parent::__construct();
206
* Starts the Session.
208
* @return boolean True if session was started
212
if ($this->started()) {
215
if (function_exists('session_write_close')) {
216
session_write_close();
218
$this->__initSession();
219
$this->__startSession();
220
return $this->started();
224
* Determine if Session has been started.
227
* @return boolean True if session has been started.
230
if (isset($_SESSION) && session_id()) {
237
* Returns true if given variable is set in session.
239
* @param string $name Variable name to check for
240
* @return boolean True if variable is there
243
function check($name) {
247
$result = Set::classicExtract($_SESSION, $name);
248
return isset($result);
252
* Returns the Session id
254
* @param id $name string
255
* @return string Session id
258
function id($id = null) {
261
session_id($this->id);
263
if ($this->started()) {
271
* Removes a variable from session.
273
* @param string $name Session variable to remove
274
* @return boolean Success
277
function delete($name) {
278
if ($this->check($name)) {
279
if (in_array($name, $this->watchKeys)) {
280
trigger_error(sprintf(__('Deleting session key {%s}', true), $name), E_USER_NOTICE);
282
$this->__overwrite($_SESSION, Set::remove($_SESSION, $name));
283
return ($this->check($name) == false);
285
$this->__setError(2, sprintf(__("%s doesn't exist", true), $name));
290
* Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself
292
* @param array $old Set of old variables => values
293
* @param array $new New set of variable => value
296
function __overwrite(&$old, $new) {
298
foreach ($old as $key => $var) {
299
if (!isset($new[$key])) {
304
foreach ($new as $key => $var) {
310
* Return error description for given error number.
312
* @param integer $errorNumber Error to set
313
* @return string Error as string
316
function __error($errorNumber) {
317
if (!is_array($this->error) || !array_key_exists($errorNumber, $this->error)) {
320
return $this->error[$errorNumber];
325
* Returns last occurred error as a string, if any.
327
* @return mixed Error description as a string, or false.
331
if ($this->lastError) {
332
return $this->__error($this->lastError);
339
* Returns true if session is valid.
341
* @return boolean Success
345
if ($this->read('Config')) {
346
if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
347
if ($this->error === false) {
351
$this->valid = false;
352
$this->__setError(1, 'Session Highjacking Attempted !!!');
359
* Returns given session variable, or all of them, if no parameters given.
361
* @param mixed $name The name of the session variable (or a path as sent to Set.extract)
362
* @return mixed The value of the session variable
365
function read($name = null) {
366
if (is_null($name)) {
367
return $this->__returnSessionVars();
372
$result = Set::classicExtract($_SESSION, $name);
374
if (!is_null($result)) {
377
$this->__setError(2, "$name doesn't exist");
382
* Returns all session variables.
384
* @return mixed Full $_SESSION array, or false on error.
387
function __returnSessionVars() {
388
if (!empty($_SESSION)) {
391
$this->__setError(2, "No Session vars set");
396
* Tells Session to write a notification when a certain session path or subpath is written to
398
* @param mixed $var The variable path to watch
402
function watch($var) {
406
if (!in_array($var, $this->watchKeys, true)) {
407
$this->watchKeys[] = $var;
412
* Tells Session to stop watching a given key path
414
* @param mixed $var The variable path to watch
418
function ignore($var) {
419
if (!in_array($var, $this->watchKeys)) {
422
foreach ($this->watchKeys as $i => $key) {
424
unset($this->watchKeys[$i]);
425
$this->watchKeys = array_values($this->watchKeys);
432
* Writes value to given session variable name.
434
* @param mixed $name Name of variable
435
* @param string $value Value to write
436
* @return boolean True if the write was successful, false if the write failed
439
function write($name, $value) {
443
if (in_array($name, $this->watchKeys)) {
444
trigger_error(sprintf(__('Writing session key {%s}: %s', true), $name, Debugger::exportVar($value)), E_USER_NOTICE);
446
$this->__overwrite($_SESSION, Set::insert($_SESSION, $name, $value));
447
return (Set::classicExtract($_SESSION, $name) === $value);
451
* Helper method to destroy invalid sessions.
457
if ($this->started()) {
461
$this->__construct($this->path);
464
$this->_checkValid();
468
* Helper method to initialize a session, based on Cake core settings.
472
function __initSession() {
473
$iniSet = function_exists('ini_set');
474
if ($iniSet && env('HTTPS')) {
475
ini_set('session.cookie_secure', 1);
477
if ($iniSet && ($this->security === 'high' || $this->security === 'medium')) {
478
ini_set('session.referer_check', $this->host);
481
if ($this->security == 'high') {
482
$this->cookieLifeTime = 0;
484
$this->cookieLifeTime = Configure::read('Session.timeout') * (Security::inactiveMins() * 60);
487
switch (Configure::read('Session.save')) {
489
if (empty($_SESSION)) {
491
ini_set('session.use_trans_sid', 0);
492
ini_set('url_rewriter.tags', '');
493
ini_set('session.serialize_handler', 'php');
494
ini_set('session.use_cookies', 1);
495
ini_set('session.name', Configure::read('Session.cookie'));
496
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
497
ini_set('session.cookie_path', $this->path);
498
ini_set('session.auto_start', 0);
499
ini_set('session.save_path', TMP . 'sessions');
504
if (empty($_SESSION)) {
505
if (Configure::read('Session.model') === null) {
506
trigger_error(__("You must set the all Configure::write('Session.*') in core.php to use database storage"), E_USER_WARNING);
510
ini_set('session.use_trans_sid', 0);
511
ini_set('url_rewriter.tags', '');
512
ini_set('session.save_handler', 'user');
513
ini_set('session.serialize_handler', 'php');
514
ini_set('session.use_cookies', 1);
515
ini_set('session.name', Configure::read('Session.cookie'));
516
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
517
ini_set('session.cookie_path', $this->path);
518
ini_set('session.auto_start', 0);
521
session_set_save_handler(
522
array('CakeSession','__open'),
523
array('CakeSession', '__close'),
524
array('CakeSession', '__read'),
525
array('CakeSession', '__write'),
526
array('CakeSession', '__destroy'),
527
array('CakeSession', '__gc')
531
if (empty($_SESSION)) {
533
ini_set('session.use_trans_sid', 0);
534
ini_set('session.name', Configure::read('Session.cookie'));
535
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
536
ini_set('session.cookie_path', $this->path);
541
if (empty($_SESSION)) {
542
if (!class_exists('Cache')) {
543
require LIBS . 'cache.php';
546
ini_set('session.use_trans_sid', 0);
547
ini_set('url_rewriter.tags', '');
548
ini_set('session.save_handler', 'user');
549
ini_set('session.use_cookies', 1);
550
ini_set('session.name', Configure::read('Session.cookie'));
551
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
552
ini_set('session.cookie_path', $this->path);
555
session_set_save_handler(
556
array('CakeSession','__open'),
557
array('CakeSession', '__close'),
558
array('Cache', 'read'),
559
array('Cache', 'write'),
560
array('Cache', 'delete'),
565
$config = CONFIGS . Configure::read('Session.save') . '.php';
567
if (is_file($config)) {
575
* Helper method to start a session
579
function __startSession() {
580
if (headers_sent()) {
581
if (empty($_SESSION)) {
585
} elseif (!isset($_SESSION)) {
586
session_cache_limiter ("must-revalidate");
588
header ('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');
597
* Helper method to create a new session.
602
function _checkValid() {
603
if ($this->read('Config')) {
604
if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
605
$time = $this->read('Config.time');
606
$this->write('Config.time', $this->sessionTime);
607
if (Configure::read('Security.level') === 'high') {
608
$check = $this->read('Config.timeout');
610
$this->write('Config.timeout', $check);
612
if (time() > ($time - (Security::inactiveMins() * Configure::read('Session.timeout')) + 2) || $check < 1) {
614
$this->write('Config.timeout', 10);
620
$this->valid = false;
621
$this->__setError(1, 'Session Highjacking Attempted !!!');
624
$this->write('Config.userAgent', $this->_userAgent);
625
$this->write('Config.time', $this->sessionTime);
626
$this->write('Config.timeout', 10);
628
$this->__setError(1, 'Session is valid');
633
* Helper method to restart a session.
638
function __regenerateId() {
639
$oldSessionId = session_id();
641
if (session_id() != ''|| isset($_COOKIE[session_name()])) {
642
setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->path);
644
session_regenerate_id(true);
645
if (PHP_VERSION < 5.1) {
646
$sessionPath = session_save_path();
647
if (empty($sessionPath)) {
648
$sessionPath = '/tmp';
650
$newSessid = session_id();
652
if (function_exists('session_write_close')) {
653
session_write_close();
655
$this->__initSession();
656
session_id($oldSessionId);
659
$file = $sessionPath . DS . 'sess_' . $oldSessionId;
661
$this->__initSession();
662
session_id($newSessid);
669
* Restarts this session.
674
$this->__regenerateId();
678
* Helper method to set an internal error message.
680
* @param integer $errorNumber Number of the error
681
* @param string $errorMessage Description of the error
685
function __setError($errorNumber, $errorMessage) {
686
if ($this->error === false) {
687
$this->error = array();
689
$this->error[$errorNumber] = $errorMessage;
690
$this->lastError = $errorNumber;
694
* Method called on open of a database session.
696
* @return boolean Success
704
* Method called on close of a database session.
706
* @return boolean Success
710
$probability = mt_rand(1, 150);
711
if ($probability <= 3) {
712
switch (Configure::read('Session.save')) {
725
* Method used to read from a database session.
727
* @param mixed $id The key of the value to read
728
* @return mixed The value of the key or false if it does not exist
731
function __read($id) {
732
$model =& ClassRegistry::getObject('Session');
734
$row = $model->find('first', array(
735
'conditions' => array($model->primaryKey => $id)
738
if (empty($row[$model->alias]['data'])) {
742
return $row[$model->alias]['data'];
746
* Helper function called on write for database sessions.
748
* @param integer $id ID that uniquely identifies session in database
749
* @param mixed $data The value of the data to be saved.
750
* @return boolean True for successful write, false otherwise.
753
function __write($id, $data) {
754
$expires = time() + Configure::read('Session.timeout') * Security::inactiveMins();
755
$model =& ClassRegistry::getObject('Session');
756
$return = $model->save(array($model->primaryKey => $id) + compact('data', 'expires'));
761
* Method called on the destruction of a database session.
763
* @param integer $id ID that uniquely identifies session in database
764
* @return boolean True for successful delete, false otherwise.
767
function __destroy($id) {
768
$model =& ClassRegistry::getObject('Session');
769
$return = $model->delete($id);
775
* Helper function called on gc for database sessions.
777
* @param integer $expires Timestamp (defaults to current time)
778
* @return boolean Success
781
function __gc($expires = null) {
782
$model =& ClassRegistry::getObject('Session');
788
$return = $model->deleteAll(array($model->alias . ".expires <" => $expires), false, false);