Session.php 9.5 KB

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