/fuel/core/classes/session/file.php

https://github.com/abdelm/stationwagon · PHP · 359 lines · 184 code · 65 blank · 110 comment · 26 complexity · 3939cf6c46f784e556771ff218cdde98 MD5 · raw file

  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. // --------------------------------------------------------------------
  14. class Session_File extends \Session_Driver
  15. {
  16. /**
  17. * array of driver config defaults
  18. */
  19. protected static $_defaults = array(
  20. 'cookie_name' => 'fuelfid', // name of the session cookie for file based sessions
  21. 'path' => '/tmp', // path where the session files should be stored
  22. 'gc_probability' => 5 // probability % (between 0 and 100) for garbage collection
  23. );
  24. // --------------------------------------------------------------------
  25. public function __construct($config = array())
  26. {
  27. // merge the driver config with the global config
  28. $this->config = array_merge($config, is_array($config['file']) ? $config['file'] : static::$_defaults);
  29. $this->config = $this->_validate_config($this->config);
  30. }
  31. // --------------------------------------------------------------------
  32. /**
  33. * create a new session
  34. *
  35. * @access public
  36. * @return Fuel\Core\Session_File
  37. */
  38. public function create()
  39. {
  40. // create a new session
  41. $this->keys['session_id'] = $this->_new_session_id();
  42. $this->keys['previous_id'] = $this->keys['session_id']; // prevents errors if previous_id has a unique index
  43. $this->keys['ip_hash'] = md5(\Input::ip().\Input::real_ip());
  44. $this->keys['user_agent'] = \Input::user_agent();
  45. $this->keys['created'] = $this->time->get_timestamp();
  46. $this->keys['updated'] = $this->keys['created'];
  47. // create the session record
  48. $this->_write_file($this->keys['session_id'], serialize(array()));
  49. // and set the session cookie
  50. $this->_set_cookie();
  51. return $this;
  52. }
  53. // --------------------------------------------------------------------
  54. /**
  55. * read the session
  56. *
  57. * @access public
  58. * @param boolean, set to true if we want to force a new session to be created
  59. * @return Fuel\Core\Session_Driver
  60. */
  61. public function read($force = false)
  62. {
  63. // get the session cookie
  64. $cookie = $this->_get_cookie();
  65. // if no session cookie was present, initialize a new session
  66. if ($cookie === false or $force)
  67. {
  68. $this->data = array();
  69. $this->keys = array();
  70. return $this;
  71. }
  72. // read the session file
  73. $payload = $this->_read_file($this->keys['session_id']);
  74. if ($payload === false)
  75. {
  76. // try to find the previous one
  77. $payload = $this->_read_file($this->keys['previous_id']);
  78. if ($payload === false)
  79. {
  80. // cookie present, but session record missing. force creation of a new session
  81. return $this->read(true);
  82. }
  83. }
  84. // unpack the payload
  85. $payload = $this->_unserialize($payload);
  86. // session referral?
  87. if (isset($payload['rotated_session_id']))
  88. {
  89. $payload = $this->_read_file($payload['rotated_session_id']);
  90. if ($payload === false)
  91. {
  92. // cookie present, but session record missing. force creation of a new session
  93. return $this->read(true);
  94. }
  95. else
  96. {
  97. // update the session
  98. $this->keys['previous_id'] = $this->keys['session_id'];
  99. $this->keys['session_id'] = $payload['rotated_session_id'];
  100. // unpack the payload
  101. $payload = $this->_unserialize($payload);
  102. }
  103. }
  104. if (isset($payload[0])) $this->data = $payload[0];
  105. if (isset($payload[1])) $this->flash = $payload[1];
  106. return parent::read();
  107. }
  108. // --------------------------------------------------------------------
  109. /**
  110. * write the session
  111. *
  112. * @access public
  113. * @return Fuel\Core\Session_File
  114. */
  115. public function write()
  116. {
  117. // do we have something to write?
  118. if ( ! empty($this->keys) or ! empty($this->data) or ! empty($this->flash))
  119. {
  120. // create the session if it doesn't exist
  121. empty($this->keys) and $this->create();
  122. parent::write();
  123. // rotate the session id if needed
  124. $this->rotate(false);
  125. // session payload
  126. $payload = $this->_serialize(array($this->data, $this->flash));
  127. // create the session file
  128. $this->_write_file($this->keys['session_id'], $payload);
  129. // was the session id rotated?
  130. if ( isset($this->keys['previous_id']) and $this->keys['previous_id'] != $this->keys['session_id'])
  131. {
  132. // point the old session file to the new one, we don't want to lose the session
  133. $payload = $this->_serialize(array('rotated_session_id' => $this->keys['session_id']));
  134. $this->_write_file($this->keys['previous_id'], $payload);
  135. }
  136. $this->_set_cookie();
  137. // do some garbage collection
  138. if (mt_rand(0,100) < $this->config['gc_probability'])
  139. {
  140. if ($handle = opendir($this->config['path']))
  141. {
  142. $expire = $this->time->get_timestamp() - $this->config['expiration_time'];
  143. while (($file = readdir($handle)) !== false)
  144. {
  145. if (filetype($this->config['path'] . $file) == 'file' and
  146. strpos($file, $this->config['cookie_name'].'_') === 0 and
  147. filemtime($this->config['path'] . $file) < $expire)
  148. {
  149. @unlink($this->config['path'] . $file);
  150. }
  151. }
  152. closedir($handle);
  153. }
  154. }
  155. }
  156. return $this;
  157. }
  158. // --------------------------------------------------------------------
  159. /**
  160. * destroy the current session
  161. *
  162. * @access public
  163. * @return Fuel\Core\Session_File
  164. */
  165. public function destroy()
  166. {
  167. // do we have something to destroy?
  168. if ( ! empty($this->keys))
  169. {
  170. // delete the session file
  171. $file = $this->config['path'].$this->config['cookie_name'].'_'.$this->keys['session_id'];
  172. if (file_exists($file))
  173. {
  174. unlink($file);
  175. }
  176. }
  177. // reset the stored session data
  178. $this->keys = $this->flash = $this->data = array();
  179. return $this;
  180. }
  181. // --------------------------------------------------------------------
  182. /**
  183. * Writes the session file
  184. *
  185. * @access private
  186. * @return boolean, true if it was an existing session, false if not
  187. */
  188. protected function _write_file($session_id, $payload)
  189. {
  190. // create the session file
  191. $file = $this->config['path'].$this->config['cookie_name'].'_'.$session_id;
  192. $exists = file_exists($file);
  193. $handle = fopen($file,'c');
  194. if ($handle)
  195. {
  196. // wait for a lock
  197. while(!flock($handle, LOCK_EX));
  198. // erase existing contents
  199. ftruncate($handle, 0);
  200. // write the session data
  201. fwrite($handle, $payload);
  202. //release the lock
  203. flock($handle, LOCK_UN);
  204. // close the file
  205. fclose($handle);
  206. }
  207. return $exists;
  208. }
  209. // --------------------------------------------------------------------
  210. /**
  211. * Reads the session file
  212. *
  213. * @access private
  214. * @return mixed, the payload if the file exists, or false if not
  215. */
  216. protected function _read_file($session_id)
  217. {
  218. $payload = false;
  219. $file = $this->config['path'].$this->config['cookie_name'].'_'.$session_id;
  220. if (file_exists($file))
  221. {
  222. $handle = fopen($file,'r');
  223. if ($handle)
  224. {
  225. // wait for a lock
  226. while(!flock($handle, LOCK_EX));
  227. // read the session data
  228. $payload = fread($handle, filesize($file));
  229. //release the lock
  230. flock($handle, LOCK_UN);
  231. // close the file
  232. fclose($handle);
  233. }
  234. }
  235. return $payload;
  236. }
  237. // --------------------------------------------------------------------
  238. /**
  239. * validate a driver config value
  240. *
  241. * @param array array with configuration values
  242. * @access public
  243. * @return array validated and consolidated config
  244. */
  245. public function _validate_config($config)
  246. {
  247. $validated = array();
  248. foreach ($config as $name => $item)
  249. {
  250. // filter out any driver config
  251. if (!is_array($item))
  252. {
  253. switch ($name)
  254. {
  255. case 'cookie_name':
  256. if ( empty($item) OR ! is_string($item))
  257. {
  258. $item = 'fuelfid';
  259. }
  260. break;
  261. case 'path':
  262. // do we have a path?
  263. if ( empty($item) OR ! is_dir($item))
  264. {
  265. throw new \FuelException('You have specify a valid path to store the session data files.');
  266. }
  267. // and can we write to it?
  268. if ( ! is_writable($item))
  269. {
  270. throw new \FuelException('The webserver doesn\'t have write access to the path to store the session data files.');
  271. }
  272. // update the path, and add the trailing slash
  273. $item = realpath($item).'/';
  274. break;
  275. case 'gc_probability':
  276. // do we have a path?
  277. if ( ! is_numeric($item) OR $item < 0 OR $item > 100)
  278. {
  279. // default value: 5%
  280. $item = 5;
  281. }
  282. break;
  283. default:
  284. // no config item for this driver
  285. break;
  286. }
  287. // global config, was validated in the driver
  288. $validated[$name] = $item;
  289. }
  290. }
  291. // validate all global settings as well
  292. return parent::_validate_config($validated);
  293. }
  294. }