SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Session.php

Copyright (C) 2017 Karmabunny Pty Ltd.

This file is a part of SproutCMS.

SproutCMS is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation, either
version 2 of the License, or (at your option) any later version.

For more information, visit <http://getsproutcms.com>.

This class was originally from Kohana 2.3.4
Copyright 2007-2008 Kohana Team
  1. <?php
  2. /**
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  *
  13.  * This class was originally from Kohana 2.3.4
  14.  * Copyright 2007-2008 Kohana Team
  15.  */
  16. namespace Sprout\Helpers;
  17.  
  18. use Kohana;
  19. use Kohana_Exception;
  20. use Event;
  21.  
  22. use Sprout\Helpers\Drivers\SessionDriver;
  23.  
  24. /**
  25.  * Session library.
  26.  */
  27. class Session
  28. {
  29.  
  30. // Session singleton
  31. protected static $instance;
  32.  
  33. // Protected key names (cannot be set by the user)
  34. protected static $protect = array('session_id', 'user_agent', 'last_activity', 'ip_address', 'total_hits', '_kf_flash_');
  35.  
  36. // Configuration and driver
  37. protected static $config;
  38. protected static $driver;
  39.  
  40. // Flash variables
  41. protected static $flash;
  42.  
  43. /**
  44.   * Singleton instance of Session.
  45.   */
  46. public static function instance()
  47. {
  48. if (Session::$instance == NULL)
  49. {
  50. // Create a new instance
  51. new Session;
  52. }
  53.  
  54. return Session::$instance;
  55. }
  56.  
  57. /**
  58.   * On first session instance creation, sets up the driver and creates session.
  59.   */
  60. public function __construct()
  61. {
  62. if (PHP_SAPI === 'cli') {
  63. $_SESSION = [];
  64. return;
  65. }
  66.  
  67. // This part only needs to be run once
  68. if (Session::$instance === NULL)
  69. {
  70. // Load config
  71. Session::$config = Kohana::config('session');
  72.  
  73. // Makes a mirrored array, eg: foo=foo
  74. Session::$protect = array_combine(Session::$protect, Session::$protect);
  75.  
  76. // Configure garbage collection
  77. ini_set('session.gc_probability', (int) Session::$config['gc_probability']);
  78. ini_set('session.gc_divisor', 100);
  79. ini_set('session.gc_maxlifetime', (Session::$config['expiration'] == 0) ? 86400 : Session::$config['expiration']);
  80.  
  81. // Create a new session
  82. static::create();
  83.  
  84. if (Session::$config['regenerate'] > 0 AND ($_SESSION['total_hits'] % Session::$config['regenerate']) === 0)
  85. {
  86. // Regenerate session id and update session cookie
  87. static::regenerate();
  88. }
  89.  
  90. // Close the session on system shutdown (run before sending the headers), so that
  91. // the session cookie(s) can be written.
  92. Event::add('system.shutdown', array($this, 'writeClose'));
  93.  
  94. // Singleton instance
  95. Session::$instance = $this;
  96. }
  97. }
  98.  
  99. /**
  100.   * Get the session id.
  101.   *
  102.   * @return string
  103.   */
  104. public static function id()
  105. {
  106. return $_SESSION['session_id'];
  107. }
  108.  
  109. /**
  110.   * Create a new session.
  111.   *
  112.   * @param array variables to set after creation
  113.   * @return void
  114.   */
  115. public static function create($vars = NULL)
  116. {
  117. // Destroy any current sessions
  118. static::destroy();
  119.  
  120. if (Session::$config['driver'] !== 'native')
  121. {
  122. // Set driver name
  123. $driver = 'Sprout\\Helpers\\Drivers\\Session\\' . ucfirst(Session::$config['driver']);
  124.  
  125. // Load the driver
  126. if (!class_exists($driver))
  127. throw new Kohana_Exception('core.driver_not_found', Session::$config['driver'], get_class());
  128.  
  129. // Initialize the driver
  130. Session::$driver = new $driver();
  131.  
  132. // Validate the driver
  133. if ( ! (Session::$driver instanceof SessionDriver))
  134. throw new Kohana_Exception('core.driver_implements', Session::$config['driver'], get_class(), 'SessionDriver');
  135.  
  136. // Register non-native driver as the session handler
  137. (
  138. array(Session::$driver, 'open'),
  139. array(Session::$driver, 'close'),
  140. array(Session::$driver, 'read'),
  141. array(Session::$driver, 'write'),
  142. array(Session::$driver, 'destroy'),
  143. array(Session::$driver, 'gc')
  144. );
  145. }
  146.  
  147. // Validate the session name
  148. if ( ! preg_match('~^(?=.*[a-z])[a-z0-9_]++$~iD', Session::$config['name']))
  149. throw new Kohana_Exception('session.invalid_session_name', Session::$config['name']);
  150.  
  151. // Name the session, this will also be the name of the cookie
  152. session_name(Session::$config['name']);
  153.  
  154. // Set the session cookie parameters
  155. (
  156. Session::$config['expiration'],
  157. Kohana::config('cookie.path'),
  158. Kohana::config('cookie.domain'),
  159. Kohana::config('cookie.secure'),
  160. true // never allow javascript to access session cookies
  161. );
  162.  
  163. // Start the session!
  164.  
  165. // Put session_id in the session variable
  166. $_SESSION['session_id'] = session_id();
  167.  
  168. // Set defaults
  169. if ( ! isset($_SESSION['_kf_flash_']))
  170. {
  171. $_SESSION['total_hits'] = 0;
  172. $_SESSION['_kf_flash_'] = array();
  173.  
  174. $_SESSION['user_agent'] = Kohana::$user_agent;
  175. $_SESSION['ip_address'] = Request::userIp();
  176. }
  177.  
  178. // Set up flash variables
  179. Session::$flash =& $_SESSION['_kf_flash_'];
  180.  
  181. // Increase total hits
  182. $_SESSION['total_hits'] += 1;
  183.  
  184. // Validate data only on hits after one
  185. if ($_SESSION['total_hits'] > 1)
  186. {
  187. // Validate the session
  188. foreach (Session::$config['validate'] as $valid)
  189. {
  190. switch ($valid)
  191. {
  192. // Check user agent for consistency
  193. case 'user_agent':
  194. if ($_SESSION[$valid] !== Kohana::$user_agent)
  195. return static::create();
  196. break;
  197.  
  198. // Check ip address for consistency
  199. case 'ip_address':
  200. if ($_SESSION[$valid] !== Request::userIp())
  201. return static::create();
  202. break;
  203.  
  204. // Check expiration time to prevent users from manually modifying it
  205. case 'expiration':
  206. if (time() - $_SESSION['last_activity'] > ini_get('session.gc_maxlifetime'))
  207. return static::create();
  208. break;
  209. }
  210. }
  211. }
  212.  
  213. // Expire flash keys
  214. static::expireFlash();
  215.  
  216. // Update last activity
  217. $_SESSION['last_activity'] = time();
  218.  
  219. // Set the new data
  220. Session::set($vars);
  221. }
  222.  
  223. /**
  224.   * Regenerates the global session id.
  225.   *
  226.   * @return void
  227.   */
  228. public static function regenerate()
  229. {
  230. if (Session::$config['driver'] === 'native')
  231. {
  232. // Generate a new session id
  233. // Note: also sets a new session cookie with the updated id
  234.  
  235. // Update session with new id
  236. $_SESSION['session_id'] = session_id();
  237. }
  238. else
  239. {
  240. // Pass the regenerating off to the driver in case it wants to do anything special
  241. $_SESSION['session_id'] = Session::$driver->regenerate();
  242. }
  243.  
  244. // Get the session name
  245. $name = session_name();
  246.  
  247. if (isset($_COOKIE[$name]))
  248. {
  249. // Change the cookie value to match the new session id to prevent "lag"
  250. Cookie::set(
  251. $name,
  252. $_SESSION['session_id'],
  253. Session::$config['expiration'],
  254. Kohana::config('cookie.path'),
  255. Kohana::config('cookie.domain'),
  256. Kohana::config('cookie.secure'),
  257. true // httpOnly flag, i.e. no javascript access
  258. );
  259. }
  260. }
  261.  
  262. /**
  263.   * Destroys the current session.
  264.   *
  265.   * @return void
  266.   */
  267. public static function destroy()
  268. {
  269. if (session_id() !== '')
  270. {
  271. // Get the session name
  272. $name = session_name();
  273.  
  274. // Destroy the session
  275.  
  276. // Re-initialize the array
  277. $_SESSION = array();
  278.  
  279. // Delete the session cookie
  280. Cookie::delete($name);
  281. }
  282. }
  283.  
  284. /**
  285.   * Runs the system.session_write event, then calls session_write_close.
  286.   *
  287.   * @return void
  288.   */
  289. public static function writeClose()
  290. {
  291. static $run;
  292.  
  293. if ($run === NULL)
  294. {
  295. $run = TRUE;
  296.  
  297. // Run the events that depend on the session being open
  298. Event::run('system.session_write');
  299.  
  300. // Expire flash keys
  301. static::expireFlash();
  302.  
  303. // Close the session
  304. }
  305. }
  306.  
  307. /**
  308.   * Set a session variable.
  309.   *
  310.   * @param string|array key, or array of values
  311.   * @param mixed value (if keys is not an array)
  312.   * @return void
  313.   */
  314. public static function set($keys, $val = FALSE)
  315. {
  316. if (empty($keys))
  317. return FALSE;
  318.  
  319. if ( ! is_array($keys))
  320. {
  321. $keys = array($keys => $val);
  322. }
  323.  
  324. foreach ($keys as $key => $val)
  325. {
  326. if (isset(Session::$protect[$key]))
  327. continue;
  328.  
  329. // Set the key
  330. $_SESSION[$key] = $val;
  331. }
  332. }
  333.  
  334. /**
  335.   * Set a flash variable.
  336.   *
  337.   * @param string|array key, or array of values
  338.   * @param mixed value (if keys is not an array)
  339.   * @return void
  340.   */
  341. public static function setFlash($keys, $val = FALSE)
  342. {
  343. if (empty($keys))
  344. return FALSE;
  345.  
  346. if ( ! is_array($keys))
  347. {
  348. $keys = array($keys => $val);
  349. }
  350.  
  351. foreach ($keys as $key => $val)
  352. {
  353. if ($key == FALSE)
  354. continue;
  355.  
  356. Session::$flash[$key] = 'new';
  357. Session::set($key, $val);
  358. }
  359. }
  360.  
  361. /**
  362.   * Freshen one, multiple or all flash variables.
  363.   *
  364.   * @param string variable key(s)
  365.   * @return void
  366.   */
  367. public static function keepFlash($keys = NULL)
  368. {
  369. $keys = ($keys === NULL) ? array_keys(Session::$flash) : func_get_args();
  370.  
  371. foreach ($keys as $key)
  372. {
  373. if (isset(Session::$flash[$key]))
  374. {
  375. Session::$flash[$key] = 'new';
  376. }
  377. }
  378. }
  379.  
  380. /**
  381.   * Expires old flash data and removes it from the session.
  382.   *
  383.   * @return void
  384.   */
  385. public static function expireFlash()
  386. {
  387. static $run;
  388.  
  389. // Method can only be run once
  390. if ($run === TRUE)
  391. return;
  392.  
  393. if ( ! empty(Session::$flash))
  394. {
  395. foreach (Session::$flash as $key => $state)
  396. {
  397. if ($state === 'old')
  398. {
  399. // Flash has expired
  400. unset(Session::$flash[$key], $_SESSION[$key]);
  401. }
  402. else
  403. {
  404. // Flash will expire
  405. Session::$flash[$key] = 'old';
  406. }
  407. }
  408. }
  409.  
  410. // Method has been run
  411. $run = TRUE;
  412. }
  413.  
  414. /**
  415.   * Get a variable. Access to sub-arrays is supported with key.subkey.
  416.   *
  417.   * @param string variable key
  418.   * @param mixed default value returned if variable does not exist
  419.   * @return mixed Variable data if key specified, otherwise array containing all session data.
  420.   */
  421. public static function get($key = FALSE, $default = FALSE)
  422. {
  423. if (empty($key))
  424. return $_SESSION;
  425.  
  426. $result = isset($_SESSION[$key]) ? $_SESSION[$key] : Kohana::keyString($_SESSION, $key);
  427.  
  428. return ($result === NULL) ? $default : $result;
  429. }
  430.  
  431.  
  432. /**
  433.   *
  434.   * @param string $key
  435.   * @return bool
  436.   */
  437. public static function has($key)
  438. {
  439. return self::get($key, null) !== null;
  440. }
  441.  
  442.  
  443. /**
  444.   * Get a variable, and delete it.
  445.   *
  446.   * @param string variable key
  447.   * @param mixed default value returned if variable does not exist
  448.   * @return mixed
  449.   */
  450. public static function getOnce($key, $default = FALSE)
  451. {
  452. $return = Session::get($key, $default);
  453. Session::delete($key);
  454.  
  455. return $return;
  456. }
  457.  
  458. /**
  459.   * Delete one or more variables.
  460.   *
  461.   * @param string variable key(s)
  462.   * @return void
  463.   */
  464. public static function delete($keys)
  465. {
  466. $args = func_get_args();
  467.  
  468. foreach ($args as $key)
  469. {
  470. if (isset(Session::$protect[$key]))
  471. continue;
  472.  
  473. // Unset the key
  474. unset($_SESSION[$key]);
  475. }
  476. }
  477.  
  478.  
  479. /**
  480.   * Removes all session variables.
  481.   */
  482. public static function deletall()
  483. {
  484. foreach ($_SESSION as $key => $_) {
  485. unset($_SESSION[$key]);
  486. }
  487. }
  488.  
  489.  
  490. /**
  491.   * Return the entire session object.
  492.   *
  493.   * @return array
  494.   */
  495. public static function list()
  496. {
  497. return $_SESSION;
  498. }
  499.  
  500. } // End Session Class
  501.