PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/session/driver.php

https://bitbucket.org/sriedel/iccrm-wip
PHP | 712 lines | 335 code | 103 blank | 274 comment | 28 complexity | 83b70cd3d9860e92c07ecf14b3f484a3 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.0
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2012 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. abstract class Session_Driver
  14. {
  15. /*
  16. * @var session class configuration
  17. */
  18. protected $config = array();
  19. /*
  20. * @var session indentification keys
  21. */
  22. protected $keys = array();
  23. /*
  24. * @var session variable data
  25. */
  26. protected $data = array();
  27. /*
  28. * @var session flash data
  29. */
  30. protected $flash = array();
  31. /*
  32. * @var session time object
  33. */
  34. protected $time = null;
  35. // --------------------------------------------------------------------
  36. // abstract methods
  37. // --------------------------------------------------------------------
  38. /**
  39. * create a new session
  40. *
  41. * @access public
  42. * @return void
  43. */
  44. abstract function create();
  45. // --------------------------------------------------------------------
  46. /**
  47. * destroy the current session
  48. *
  49. * @access public
  50. * @return void
  51. */
  52. abstract function destroy();
  53. // --------------------------------------------------------------------
  54. // generic driver methods
  55. // --------------------------------------------------------------------
  56. /**
  57. * read the session
  58. *
  59. * @access public
  60. * @return Fuel\Core\Session_Driver
  61. */
  62. public function read()
  63. {
  64. // do we need to create a new session?
  65. empty($this->keys) and $this->create();
  66. // mark the loaded flash data, auto-expire if configured
  67. foreach($this->flash as $key => $value)
  68. {
  69. if ($this->config['flash_auto_expire'] === true)
  70. {
  71. $this->flash[$key]['state'] = 'expire';
  72. }
  73. else
  74. {
  75. $this->flash[$key]['state'] = 'loaded';
  76. }
  77. }
  78. return $this;
  79. }
  80. // --------------------------------------------------------------------
  81. /**
  82. * write the session
  83. *
  84. * @access public
  85. * @return Fuel\Core\Session_Driver
  86. */
  87. public function write()
  88. {
  89. $this->_cleanup_flash();
  90. return $this;
  91. }
  92. // --------------------------------------------------------------------
  93. /**
  94. * generic driver initialisation
  95. *
  96. * @access public
  97. * @return void
  98. */
  99. public function init()
  100. {
  101. // get a time object
  102. $this->time = \Date::time();
  103. }
  104. // --------------------------------------------------------------------
  105. /**
  106. * set session variables
  107. *
  108. * @param string|array name of the variable to set or array of values, array(name => value)
  109. * @param mixed value
  110. * @access public
  111. * @return Fuel\Core\Session_Driver
  112. */
  113. public function set($name, $value = null)
  114. {
  115. is_null($name) or \Arr::set($this->data, $name, $value);
  116. return $this;
  117. }
  118. // --------------------------------------------------------------------
  119. /**
  120. * get session variables
  121. *
  122. * @access public
  123. * @param string name of the variable to get
  124. * @param mixed default value to return if the variable does not exist
  125. * @return mixed
  126. */
  127. public function get($name, $default = null)
  128. {
  129. if (is_null($name))
  130. {
  131. return $this->data;
  132. }
  133. return \Arr::get($this->data, $name, $default);
  134. }
  135. // --------------------------------------------------------------------
  136. /**
  137. * get session key variables
  138. *
  139. * @access public
  140. * @param string name of the variable to get, default is 'session_id'
  141. * @return mixed contents of the requested variable, or false if not found
  142. */
  143. public function key($name = 'session_id')
  144. {
  145. return isset($this->keys[$name]) ? $this->keys[$name] : false;
  146. }
  147. // --------------------------------------------------------------------
  148. /**
  149. * delete session variables
  150. *
  151. * @param string name of the variable to delete
  152. * @param mixed value
  153. * @access public
  154. * @return Fuel\Core\Session_Driver
  155. */
  156. public function delete($name)
  157. {
  158. \Arr::delete($this->data, $name);
  159. return $this;
  160. }
  161. // --------------------------------------------------------------------
  162. /**
  163. * force a session_id rotation
  164. *
  165. * @access public
  166. * @param boolean, if true, force a session id rotation
  167. * @return Fuel\Core\Session_Driver
  168. */
  169. public function rotate($force = true)
  170. {
  171. // do we have a session?
  172. if ( ! empty($this->keys))
  173. {
  174. // existing session. need to rotate the session id?
  175. if ($force or ($this->config['rotation_time'] and $this->keys['created'] + $this->config['rotation_time'] <= $this->time->get_timestamp()))
  176. {
  177. // generate a new session id, and update the create timestamp
  178. $this->keys['previous_id'] = $this->keys['session_id'];
  179. $this->keys['session_id'] = $this->_new_session_id();
  180. $this->keys['created'] = $this->time->get_timestamp();
  181. $this->keys['updated'] = $this->keys['created'];
  182. }
  183. }
  184. return $this;
  185. }
  186. // --------------------------------------------------------------------
  187. /**
  188. * set session flash variables
  189. *
  190. * @param string name of the variable to set
  191. * @param mixed value
  192. * @access public
  193. * @return Fuel\Core\Session_Driver
  194. */
  195. public function set_flash($name, $value)
  196. {
  197. $this->flash[$this->config['flash_id'].'::'.$name] = array('state' => 'new', 'value' => $value);
  198. return $this;
  199. }
  200. // --------------------------------------------------------------------
  201. /**
  202. * get session flash variables
  203. *
  204. * @access public
  205. * @param string name of the variable to get
  206. * @param mixed default value to return if the variable does not exist
  207. * @return mixed
  208. */
  209. public function get_flash($name, $default = null)
  210. {
  211. if (is_null($name))
  212. {
  213. $default = array();
  214. foreach($this->flash as $key => $value)
  215. {
  216. $key = substr($key, strpos($key, '::')+2);
  217. $default[$key] = $value;
  218. }
  219. }
  220. else
  221. {
  222. // check if we need to run an Arr:get()
  223. if (strpos($name, '.') !== false)
  224. {
  225. $keys = explode('.', $name, 2);
  226. $name = array_shift($keys);
  227. }
  228. else
  229. {
  230. $keys = false;
  231. }
  232. if (isset($this->flash[$this->config['flash_id'].'::'.$name]))
  233. {
  234. // if it's not a var set in this request, mark it for expiration
  235. if ($this->flash[$this->config['flash_id'].'::'.$name]['state'] !== 'new')
  236. {
  237. $this->flash[$this->config['flash_id'].'::'.$name]['state'] = 'expire';
  238. }
  239. if ($keys)
  240. {
  241. $default = \Arr::get($this->flash[$this->config['flash_id'].'::'.$name]['value'], $keys[0], $default);
  242. }
  243. else
  244. {
  245. $default = $this->flash[$this->config['flash_id'].'::'.$name]['value'];
  246. }
  247. }
  248. }
  249. return ($default instanceof \Closure) ? $default() : $default;
  250. }
  251. // --------------------------------------------------------------------
  252. /**
  253. * keep session flash variables
  254. *
  255. * @access public
  256. * @param string name of the variable to keep
  257. * @return Fuel\Core\Session_Driver
  258. */
  259. public function keep_flash($name)
  260. {
  261. if (is_null($name))
  262. {
  263. foreach($this->flash as $key => $value)
  264. {
  265. $this->flash[$key]['state'] = 'new';
  266. }
  267. }
  268. elseif (isset($this->flash[$this->config['flash_id'].'::'.$name]))
  269. {
  270. $this->flash[$this->config['flash_id'].'::'.$name]['state'] = 'new';
  271. }
  272. return $this;
  273. }
  274. // --------------------------------------------------------------------
  275. /**
  276. * delete session flash variables
  277. *
  278. * @param string name of the variable to delete
  279. * @param mixed value
  280. * @access public
  281. * @return Fuel\Core\Session_Driver
  282. */
  283. public function delete_flash($name)
  284. {
  285. if (is_null($name))
  286. {
  287. $this->flash = array();
  288. }
  289. elseif (isset($this->flash[$this->config['flash_id'].'::'.$name]))
  290. {
  291. unset($this->flash[$this->config['flash_id'].'::'.$name]);
  292. }
  293. return $this;
  294. }
  295. // --------------------------------------------------------------------
  296. /**
  297. * set the session flash id
  298. *
  299. * @param string name of the id to set
  300. * @access public
  301. * @return Fuel\Core\Session_Driver
  302. */
  303. public function set_flash_id($name)
  304. {
  305. $this->config['flash_id'] = (string) $name;
  306. return $this;
  307. }
  308. // --------------------------------------------------------------------
  309. /**
  310. * get the current session flash id
  311. *
  312. * @access public
  313. * @return string name of the flash id
  314. */
  315. public function get_flash_id()
  316. {
  317. return $this->config['flash_id'];
  318. }
  319. // --------------------------------------------------------------------
  320. /**
  321. * get a runtime config value
  322. *
  323. * @param string name of the config variable to get
  324. * @access public
  325. * @return mixed
  326. */
  327. public function get_config($name)
  328. {
  329. return isset($this->config[$name]) ? $this->config[$name] : null;
  330. }
  331. // --------------------------------------------------------------------
  332. /**
  333. * set a runtime config value
  334. *
  335. * @param string name of the config variable to set
  336. * @access public
  337. * @return Fuel\Core\Session_Driver
  338. */
  339. public function set_config($name, $value = null)
  340. {
  341. if (isset($this->config[$name])) $this->config[$name] = $value;
  342. return $this;
  343. }
  344. // --------------------------------------------------------------------
  345. /**
  346. * removes flash variables marked as old
  347. *
  348. * @access private
  349. * @return void
  350. */
  351. protected function _cleanup_flash()
  352. {
  353. foreach($this->flash as $key => $value)
  354. {
  355. if ($value['state'] === 'expire')
  356. {
  357. unset($this->flash[$key]);
  358. }
  359. }
  360. }
  361. // --------------------------------------------------------------------
  362. /**
  363. * generate a new session id
  364. *
  365. * @access private
  366. * @return void
  367. */
  368. protected function _new_session_id()
  369. {
  370. $session_id = '';
  371. while (strlen($session_id) < 32)
  372. {
  373. $session_id .= mt_rand(0, mt_getrandmax());
  374. }
  375. return md5(uniqid($session_id, TRUE));
  376. }
  377. // --------------------------------------------------------------------
  378. /**
  379. * write a cookie
  380. *
  381. * @access private
  382. * @param array, cookie payload
  383. * @return void
  384. */
  385. protected function _set_cookie($payload = array())
  386. {
  387. // record the last update time of the session
  388. $this->keys['updated'] = $this->time->get_timestamp();
  389. // add the session keys to the payload
  390. array_unshift($payload, $this->keys);
  391. // encrypt the payload
  392. $payload = \Crypt::encode($this->_serialize($payload));
  393. // make sure it doesn't exceed the cookie size specification
  394. if (strlen($payload) > 4000)
  395. {
  396. throw new \FuelException('The session data stored by the application in the cookie exceeds 4Kb. Select a different session storage driver.');
  397. }
  398. // write the session cookie
  399. if ($this->config['expire_on_close'])
  400. {
  401. return \Cookie::set($this->config['cookie_name'], $payload, 0, $this->config['cookie_path'], $this->config['cookie_domain'], null, $this->config['cookie_http_only']);
  402. }
  403. else
  404. {
  405. return \Cookie::set($this->config['cookie_name'], $payload, $this->config['expiration_time'], $this->config['cookie_path'], $this->config['cookie_domain'], null, $this->config['cookie_http_only']);
  406. }
  407. }
  408. // --------------------------------------------------------------------
  409. /**
  410. * read a cookie
  411. *
  412. * @access private
  413. * @return void
  414. */
  415. protected function _get_cookie()
  416. {
  417. // was the cookie posted?
  418. $cookie = \Input::post($this->config['post_cookie_name'], false);
  419. // if not found, fetch the regular cookie
  420. if ($cookie === false)
  421. {
  422. $cookie = \Cookie::get($this->config['cookie_name'], false);
  423. }
  424. if ($cookie !== false)
  425. {
  426. // fetch the payload
  427. $cookie = $this->_unserialize(\Crypt::decode($cookie));
  428. // validate the cookie
  429. if ( ! isset($cookie[0]) )
  430. {
  431. // not a valid cookie payload
  432. }
  433. elseif ($cookie[0]['updated'] + $this->config['expiration_time'] <= $this->time->get_timestamp())
  434. {
  435. // session has expired
  436. }
  437. elseif ($this->config['match_ip'] and $cookie[0]['ip_hash'] !== md5(\Input::ip().\Input::real_ip()))
  438. {
  439. // IP address doesn't match
  440. }
  441. elseif ($this->config['match_ua'] and $cookie[0]['user_agent'] !== \Input::user_agent())
  442. {
  443. // user agent doesn't match
  444. }
  445. else
  446. {
  447. // session is valid, retrieve the session keys
  448. if (isset($cookie[0])) $this->keys = $cookie[0];
  449. // and return the cookie payload
  450. array_shift($cookie);
  451. return $cookie;
  452. }
  453. }
  454. // no payload
  455. return false;
  456. }
  457. // --------------------------------------------------------------------
  458. /**
  459. * Serialize an array
  460. *
  461. * This function first converts any slashes found in the array to a temporary
  462. * marker, so when it gets unserialized the slashes will be preserved
  463. *
  464. * @access private
  465. * @param array
  466. * @return string
  467. */
  468. protected function _serialize($data)
  469. {
  470. if (is_array($data))
  471. {
  472. foreach ($data as $key => $val)
  473. {
  474. if (is_string($val))
  475. {
  476. $data[$key] = str_replace('\\', '{{slash}}', $val);
  477. }
  478. }
  479. }
  480. else
  481. {
  482. if (is_string($data))
  483. {
  484. $data = str_replace('\\', '{{slash}}', $data);
  485. }
  486. }
  487. return serialize($data);
  488. }
  489. // --------------------------------------------------------------------
  490. /**
  491. * Unserialize
  492. *
  493. * This function unserializes a data string, then converts any
  494. * temporary slash markers back to actual slashes
  495. *
  496. * @access private
  497. * @param array
  498. * @return string
  499. */
  500. protected function _unserialize($data)
  501. {
  502. $data = @unserialize($data);
  503. if (is_array($data))
  504. {
  505. foreach ($data as $key => $val)
  506. {
  507. if (is_string($val))
  508. {
  509. $data[$key] = str_replace('{{slash}}', '\\', $val);
  510. }
  511. }
  512. return $data;
  513. }
  514. return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
  515. }
  516. // --------------------------------------------------------------------
  517. /**
  518. * validate__config
  519. *
  520. * This function validates all global (driver independent) configuration values
  521. *
  522. * @access private
  523. * @param array
  524. * @return array
  525. */
  526. protected function _validate_config($config)
  527. {
  528. $validated = array();
  529. foreach ($config as $name => $item)
  530. {
  531. switch($name)
  532. {
  533. case 'driver':
  534. // if we get here, this one was ok... ;-)
  535. break;
  536. case 'match_ip':
  537. // make sure it's a boolean
  538. $item = (bool) $item;
  539. break;
  540. case 'match_ua':
  541. // make sure it's a boolean
  542. $item = (bool) $item;
  543. break;
  544. case 'cookie_domain':
  545. // make sure it's a string
  546. $item = (string) $item;
  547. break;
  548. case 'cookie_path':
  549. // make sure it's a string
  550. $item = (string) $item;
  551. if (empty($item))
  552. {
  553. $item = '/';
  554. }
  555. break;
  556. case 'cookie_http_only':
  557. // make sure it's a boolean
  558. $item = (bool) $item;
  559. break;
  560. case 'expire_on_close':
  561. // make sure it's a boolean
  562. $item = (bool) $item;
  563. break;
  564. case 'expiration_time':
  565. // make sure it's an integer
  566. $item = (int) $item;
  567. if ($item <= 0)
  568. {
  569. // invalid? set it to two years from now
  570. $item = 86400 * 365 * 2;
  571. }
  572. break;
  573. case 'rotation_time':
  574. // make sure it's an integer
  575. $item = (int) $item;
  576. if ($item <= 0)
  577. {
  578. // invalid? set it to 5 minutes
  579. $item = 300;
  580. }
  581. break;
  582. case 'flash_id':
  583. // make sure it's a string
  584. $item = (string) $item;
  585. if (empty($item))
  586. {
  587. $item = 'flash';
  588. }
  589. break;
  590. case 'flash_auto_expire':
  591. // make sure it's a boolean
  592. $item = (bool) $item;
  593. break;
  594. case 'post_cookie_name':
  595. // make sure it's a string
  596. $item = (string) $item;
  597. break;
  598. default:
  599. // ignore this setting
  600. break;
  601. }
  602. // store the validated result
  603. $validated[$name] = $item;
  604. }
  605. return $validated;
  606. }
  607. }