1<?php defined('SYSPATH') OR die('No direct script access.'); 2/** 3 * Base session class. 4 * 5 * @package Kohana 6 * @category Session 7 * @author Kohana Team 8 * @copyright (c) 2008-2012 Kohana Team 9 * @license http://kohanaframework.org/license 10 */ 11abstract class Kohana_Session { 12 13 /** 14 * @var string default session adapter 15 */ 16 public static $default = 'native'; 17 18 /** 19 * @var array session instances 20 */ 21 public static $instances = array(); 22 23 /** 24 * Creates a singleton session of the given type. Some session types 25 * (native, database) also support restarting a session by passing a 26 * session id as the second parameter. 27 * 28 * $session = Session::instance(); 29 * 30 * [!!] [Session::write] will automatically be called when the request ends. 31 * 32 * @param string $type type of session (native, cookie, etc) 33 * @param string $id session identifier 34 * @return Session 35 * @uses Kohana::$config 36 */ 37 public static function instance($type = NULL, $id = NULL) 38 { 39 if ($type === NULL) 40 { 41 // Use the default type 42 $type = Session::$default; 43 } 44 45 if ( ! isset(Session::$instances[$type])) 46 { 47 // Load the configuration for this type 48 $config = Kohana::$config->load('session')->get($type); 49 50 // Set the session class name 51 $class = 'Session_'.ucfirst($type); 52 53 // Create a new session instance 54 Session::$instances[$type] = $session = new $class($config, $id); 55 56 // Write the session at shutdown 57 register_shutdown_function(array($session, 'write')); 58 } 59 60 return Session::$instances[$type]; 61 } 62 63 /** 64 * @var string cookie name 65 */ 66 protected $_name = 'session'; 67 68 /** 69 * @var int cookie lifetime 70 */ 71 protected $_lifetime = 0; 72 73 /** 74 * @var bool encrypt session data? 75 */ 76 protected $_encrypted = FALSE; 77 78 /** 79 * @var array session data 80 */ 81 protected $_data = array(); 82 83 /** 84 * @var bool session destroyed? 85 */ 86 protected $_destroyed = FALSE; 87 88 /** 89 * Overloads the name, lifetime, and encrypted session settings. 90 * 91 * [!!] Sessions can only be created using the [Session::instance] method. 92 * 93 * @param array $config configuration 94 * @param string $id session id 95 * @return void 96 * @uses Session::read 97 */ 98 public function __construct(array $config = NULL, $id = NULL) 99 { 100 if (isset($config['name'])) 101 { 102 // Cookie name to store the session id in 103 $this->_name = (string) $config['name']; 104 } 105 106 if (isset($config['lifetime'])) 107 { 108 // Cookie lifetime 109 $this->_lifetime = (int) $config['lifetime']; 110 } 111 112 if (isset($config['encrypted'])) 113 { 114 if ($config['encrypted'] === TRUE) 115 { 116 // Use the default Encrypt instance 117 $config['encrypted'] = 'default'; 118 } 119 120 // Enable or disable encryption of data 121 $this->_encrypted = $config['encrypted']; 122 } 123 124 // Load the session 125 $this->read($id); 126 } 127 128 /** 129 * Session object is rendered to a serialized string. If encryption is 130 * enabled, the session will be encrypted. If not, the output string will 131 * be encoded. 132 * 133 * echo $session; 134 * 135 * @return string 136 * @uses Encrypt::encode 137 */ 138 public function __toString() 139 { 140 // Serialize the data array 141 $data = $this->_serialize($this->_data); 142 143 if ($this->_encrypted) 144 { 145 // Encrypt the data using the default key 146 $data = Encrypt::instance($this->_encrypted)->encode($data); 147 } 148 else 149 { 150 // Encode the data 151 $data = $this->_encode($data); 152 } 153 154 return $data; 155 } 156 157 /** 158 * Returns the current session array. The returned array can also be 159 * assigned by reference. 160 * 161 * // Get a copy of the current session data 162 * $data = $session->as_array(); 163 * 164 * // Assign by reference for modification 165 * $data =& $session->as_array(); 166 * 167 * @return array 168 */ 169 public function & as_array() 170 { 171 return $this->_data; 172 } 173 174 /** 175 * Get the current session id, if the session supports it. 176 * 177 * $id = $session->id(); 178 * 179 * [!!] Not all session types have ids. 180 * 181 * @return string 182 * @since 3.0.8 183 */ 184 public function id() 185 { 186 return NULL; 187 } 188 189 /** 190 * Get the current session cookie name. 191 * 192 * $name = $session->name(); 193 * 194 * @return string 195 * @since 3.0.8 196 */ 197 public function name() 198 { 199 return $this->_name; 200 } 201 202 /** 203 * Get a variable from the session array. 204 * 205 * $foo = $session->get('foo'); 206 * 207 * @param string $key variable name 208 * @param mixed $default default value to return 209 * @return mixed 210 */ 211 public function get($key, $default = NULL) 212 { 213 return array_key_exists($key, $this->_data) ? $this->_data[$key] : $default; 214 } 215 216 /** 217 * Get and delete a variable from the session array. 218 * 219 * $bar = $session->get_once('bar'); 220 * 221 * @param string $key variable name 222 * @param mixed $default default value to return 223 * @return mixed 224 */ 225 public function get_once($key, $default = NULL) 226 { 227 $value = $this->get($key, $default); 228 229 unset($this->_data[$key]); 230 231 return $value; 232 } 233 234 /** 235 * Set a variable in the session array. 236 * 237 * $session->set('foo', 'bar'); 238 * 239 * @param string $key variable name 240 * @param mixed $value value 241 * @return $this 242 */ 243 public function set($key, $value) 244 { 245 $this->_data[$key] = $value; 246 247 return $this; 248 } 249 250 /** 251 * Set a variable by reference. 252 * 253 * $session->bind('foo', $foo); 254 * 255 * @param string $key variable name 256 * @param mixed $value referenced value 257 * @return $this 258 */ 259 public function bind($key, & $value) 260 { 261 $this->_data[$key] =& $value; 262 263 return $this; 264 } 265 266 /** 267 * Removes a variable in the session array. 268 * 269 * $session->delete('foo'); 270 * 271 * @param string $key,... variable name 272 * @return $this 273 */ 274 public function delete($key) 275 { 276 $args = func_get_args(); 277 278 foreach ($args as $key) 279 { 280 unset($this->_data[$key]); 281 } 282 283 return $this; 284 } 285 286 /** 287 * Loads existing session data. 288 * 289 * $session->read(); 290 * 291 * @param string $id session id 292 * @return void 293 */ 294 public function read($id = NULL) 295 { 296 $data = NULL; 297 298 try 299 { 300 if (is_string($data = $this->_read($id))) 301 { 302 if ($this->_encrypted) 303 { 304 // Decrypt the data using the default key 305 $data = Encrypt::instance($this->_encrypted)->decode($data); 306 } 307 else 308 { 309 // Decode the data 310 $data = $this->_decode($data); 311 } 312 313 // Unserialize the data 314 $data = $this->_unserialize($data); 315 } 316 else 317 { 318 // Ignore these, session is valid, likely no data though. 319 } 320 } 321 catch (Exception $e) 322 { 323 // Error reading the session, usually a corrupt session. 324 throw new Session_Exception('Error reading session data.', NULL, Session_Exception::SESSION_CORRUPT); 325 } 326 327 if (is_array($data)) 328 { 329 // Load the data locally 330 $this->_data = $data; 331 } 332 } 333 334 /** 335 * Generates a new session id and returns it. 336 * 337 * $id = $session->regenerate(); 338 * 339 * @return string 340 */ 341 public function regenerate() 342 { 343 return $this->_regenerate(); 344 } 345 346 /** 347 * Sets the last_active timestamp and saves the session. 348 * 349 * $session->write(); 350 * 351 * [!!] Any errors that occur during session writing will be logged, 352 * but not displayed, because sessions are written after output has 353 * been sent. 354 * 355 * @return boolean 356 * @uses Kohana::$log 357 */ 358 public function write() 359 { 360 if (headers_sent() OR $this->_destroyed) 361 { 362 // Session cannot be written when the headers are sent or when 363 // the session has been destroyed 364 return FALSE; 365 } 366 367 // Set the last active timestamp 368 $this->_data['last_active'] = time(); 369 370 try 371 { 372 return $this->_write(); 373 } 374 catch (Exception $e) 375 { 376 // Log & ignore all errors when a write fails 377 Kohana::$log->add(Log::ERROR, Kohana_Exception::text($e))->write(); 378 379 return FALSE; 380 } 381 } 382 383 /** 384 * Completely destroy the current session. 385 * 386 * $success = $session->destroy(); 387 * 388 * @return boolean 389 */ 390 public function destroy() 391 { 392 if ($this->_destroyed === FALSE) 393 { 394 if ($this->_destroyed = $this->_destroy()) 395 { 396 // The session has been destroyed, clear all data 397 $this->_data = array(); 398 } 399 } 400 401 return $this->_destroyed; 402 } 403 404 /** 405 * Restart the session. 406 * 407 * $success = $session->restart(); 408 * 409 * @return boolean 410 */ 411 public function restart() 412 { 413 if ($this->_destroyed === FALSE) 414 { 415 // Wipe out the current session. 416 $this->destroy(); 417 } 418 419 // Allow the new session to be saved 420 $this->_destroyed = FALSE; 421 422 return $this->_restart(); 423 } 424 425 /** 426 * Serializes the session data. 427 * 428 * @param array $data data 429 * @return string 430 */ 431 protected function _serialize($data) 432 { 433 return serialize($data); 434 } 435 436 /** 437 * Unserializes the session data. 438 * 439 * @param string $data data 440 * @return array 441 */ 442 protected function _unserialize($data) 443 { 444 return unserialize($data); 445 } 446 447 /** 448 * Encodes the session data using [base64_encode]. 449 * 450 * @param string $data data 451 * @return string 452 */ 453 protected function _encode($data) 454 { 455 return base64_encode($data); 456 } 457 458 /** 459 * Decodes the session data using [base64_decode]. 460 * 461 * @param string $data data 462 * @return string 463 */ 464 protected function _decode($data) 465 { 466 return base64_decode($data); 467 } 468 469 /** 470 * Loads the raw session data string and returns it. 471 * 472 * @param string $id session id 473 * @return string 474 */ 475 abstract protected function _read($id = NULL); 476 477 /** 478 * Generate a new session id and return it. 479 * 480 * @return string 481 */ 482 abstract protected function _regenerate(); 483 484 /** 485 * Writes the current session. 486 * 487 * @return boolean 488 */ 489 abstract protected function _write(); 490 491 /** 492 * Destroys the current session. 493 * 494 * @return boolean 495 */ 496 abstract protected function _destroy(); 497 498 /** 499 * Restarts the current session. 500 * 501 * @return boolean 502 */ 503 abstract protected function _restart(); 504 505} 506