PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/session/file.php

http://github.com/fuel/core
PHP | 371 lines | 212 code | 55 blank | 104 comment | 27 complexity | c358b9bbe1a4e8897ee59398dc9f31a9 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Fuel is a fast, lightweight, community driven PHP 5.4+ framework.
  4. *
  5. * @package Fuel
  6. * @version 1.9-dev
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2019 Fuel Development Team
  10. * @link https://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. parent::__construct($config);
  28. // merge the driver config with the global config
  29. $this->config = array_merge($config, is_array($config['file']) ? $config['file'] : static::$_defaults);
  30. $this->config = $this->_validate_config($this->config);
  31. }
  32. // --------------------------------------------------------------------
  33. /**
  34. * Garbage Collector
  35. *
  36. * @return bool
  37. */
  38. public function gc()
  39. {
  40. // do some garbage collection
  41. if (mt_rand(0, 100) < $this->config['gc_probability'])
  42. {
  43. if ($handle = opendir($this->config['path']))
  44. {
  45. $expire = $this->time->get_timestamp() - $this->config['expiration_time'];
  46. while (($file = readdir($handle)) !== false)
  47. {
  48. if (filetype($this->config['path'] . $file) == 'file' and
  49. strpos($file, $this->config['cookie_name'].'_') === 0 and
  50. filemtime($this->config['path'] . $file) < $expire)
  51. {
  52. @unlink($this->config['path'] . $file);
  53. }
  54. }
  55. closedir($handle);
  56. }
  57. }
  58. return true;
  59. }
  60. // --------------------------------------------------------------------
  61. /**
  62. * destroy the current session
  63. *
  64. * @return \Session_File
  65. */
  66. public function destroy()
  67. {
  68. // do we have something to destroy?
  69. if ( ! empty($this->keys))
  70. {
  71. // delete the session file
  72. $file = $this->config['path'].$this->config['cookie_name'].'_'.$this->keys['session_id'];
  73. if (is_file($file))
  74. {
  75. unlink($file);
  76. }
  77. }
  78. parent::destroy();
  79. return $this;
  80. }
  81. // --------------------------------------------------------------------
  82. /**
  83. * read the session
  84. *
  85. * @param boolean, set to true if we want to force a new session to be created
  86. * @return \Session_Driver
  87. */
  88. protected function read($force = false)
  89. {
  90. // get the session cookie
  91. $cookie = $this->_get_cookie();
  92. // if a cookie was present, find the session record
  93. if ($cookie and ! $force and isset($cookie[0]))
  94. {
  95. // read the session file
  96. $payload = $this->_read_file($cookie[0]);
  97. if ($payload === false)
  98. {
  99. // cookie present, but session record missing. force creation of a new session
  100. return $this->read(true);
  101. }
  102. // unpack the payload
  103. $payload = $this->_unserialize($payload);
  104. // session referral?
  105. if (isset($payload['rotated_session_id']))
  106. {
  107. $payload = $this->_read_file($payload['rotated_session_id']);
  108. if ($payload === false)
  109. {
  110. // cookie present, but session record missing. force creation of a new session
  111. return $this->read(true);
  112. }
  113. else
  114. {
  115. // unpack the payload
  116. $payload = $this->_unserialize($payload);
  117. }
  118. }
  119. if ( ! isset($payload[0]) or ! is_array($payload[0]))
  120. {
  121. logger('DEBUG', 'Error: not a valid session file payload!');
  122. }
  123. elseif ($payload[0]['updated'] + $this->config['expiration_time'] <= $this->time->get_timestamp())
  124. {
  125. logger('DEBUG', 'Error: session id has expired!');
  126. }
  127. elseif ($this->config['match_ip'] and $payload[0]['ip_hash'] !== md5(\Input::ip().\Input::real_ip()))
  128. {
  129. logger('DEBUG', 'Error: IP address in the session doesn\'t match this requests source IP!');
  130. }
  131. elseif ($this->config['match_ua'] and $payload[0]['user_agent'] !== \Input::user_agent())
  132. {
  133. logger('DEBUG', 'Error: User agent in the session doesn\'t match the browsers user agent string!');
  134. }
  135. else
  136. {
  137. // session is valid, retrieve the payload
  138. if (isset($payload[0]) and is_array($payload[0]))
  139. {
  140. $this->keys = $payload[0];
  141. }
  142. if (isset($payload[1]) and is_array($payload[1]))
  143. {
  144. $this->data = $payload[1];
  145. }
  146. if (isset($payload[2]) and is_array($payload[2]))
  147. {
  148. $this->flash = $payload[2];
  149. }
  150. }
  151. }
  152. return $this;
  153. }
  154. // --------------------------------------------------------------------
  155. /**
  156. * write the session
  157. *
  158. * @return \Session_File
  159. */
  160. protected function write()
  161. {
  162. // do we have something to write?
  163. if ( ! empty($this->keys) or ! empty($this->data) or ! empty($this->flash))
  164. {
  165. // rotate the session id if needed
  166. $this->rotate(false);
  167. // record the last update time of the session
  168. $this->keys['updated'] = $this->time->get_timestamp();
  169. // session payload
  170. $payload = $this->_serialize(array($this->keys, $this->data, $this->flash));
  171. // create the session file
  172. $this->_write_file($this->keys['session_id'], $payload);
  173. // was the session id rotated?
  174. if ( isset($this->keys['previous_id']) and $this->keys['previous_id'] != $this->keys['session_id'])
  175. {
  176. // point the old session file to the new one, we don't want to lose the session
  177. $payload = $this->_serialize(array('rotated_session_id' => $this->keys['session_id']));
  178. $this->_write_file($this->keys['previous_id'], $payload);
  179. }
  180. // then update the cookie
  181. $this->_set_cookie(array($this->keys['session_id']));
  182. // Run garbage collector
  183. $this->gc();
  184. }
  185. return $this;
  186. }
  187. // --------------------------------------------------------------------
  188. /**
  189. * Writes the session file
  190. *
  191. * @param $session_id
  192. * @param $payload
  193. * @return boolean, true if it was an existing session, false if not
  194. * @throws \FuelException
  195. */
  196. protected function _write_file($session_id, $payload)
  197. {
  198. // create the session file
  199. $file = $this->config['path'].$this->config['cookie_name'].'_'.$session_id;
  200. $exists = is_file($file);
  201. $handle = fopen($file, 'c');
  202. if ($handle)
  203. {
  204. // wait for a lock
  205. while( ! flock($handle, LOCK_EX));
  206. // erase existing contents
  207. ftruncate($handle, 0);
  208. // write the session data
  209. fwrite($handle, $payload);
  210. // flush any pending output
  211. fflush($handle);
  212. // release the lock
  213. flock($handle, LOCK_UN);
  214. // close the file
  215. fclose($handle);
  216. }
  217. else
  218. {
  219. throw new \FuelException('Could not open the session file in "'.$this->config['path']." for write access");
  220. }
  221. return $exists;
  222. }
  223. // --------------------------------------------------------------------
  224. /**
  225. * Reads the session file
  226. *
  227. * @param $session_id
  228. * @return mixed, the payload if the file exists, or false if not
  229. */
  230. protected function _read_file($session_id)
  231. {
  232. $payload = false;
  233. $file = $this->config['path'].$this->config['cookie_name'].'_'.$session_id;
  234. // normalize the file
  235. $file = realpath($file);
  236. // make sure it exists and is in the config path
  237. if (is_file($file) and strpos($file, $this->config['path']) === 0)
  238. {
  239. $handle = fopen($file, 'r');
  240. if ($handle)
  241. {
  242. // wait for a lock
  243. while( ! flock($handle, LOCK_SH));
  244. // read the session data
  245. $payload = file_get_contents($file);
  246. //release the lock
  247. flock($handle, LOCK_UN);
  248. // close the file
  249. fclose($handle);
  250. }
  251. }
  252. // only return the payload if it looks like a serialized array
  253. return strpos($payload, 'a:') === 0 ? $payload : false;
  254. }
  255. // --------------------------------------------------------------------
  256. /**
  257. * validate a driver config value
  258. *
  259. * @param array $config array with configuration values
  260. * @return array validated and consolidated config
  261. * @throws \FuelException
  262. */
  263. public function _validate_config($config)
  264. {
  265. $validated = array();
  266. foreach ($config as $name => $item)
  267. {
  268. // filter out any driver config
  269. if (!is_array($item))
  270. {
  271. switch ($name)
  272. {
  273. case 'cookie_name':
  274. if ( empty($item) or ! is_string($item))
  275. {
  276. $item = 'fuelfid';
  277. }
  278. break;
  279. case 'path':
  280. // do we have a path?
  281. if ( empty($item) or ! is_dir($item))
  282. {
  283. throw new \FuelException('You have specify a valid path to store the session data files.');
  284. }
  285. // and can we write to it?
  286. if ( ! is_writable($item))
  287. {
  288. throw new \FuelException('The webserver doesn\'t have write access to the path to store the session data files.');
  289. }
  290. // update the path, unify the slashes, and add the trailing slash
  291. $item = realpath(str_replace(array('/', '\\'), DS, $item)).DS;
  292. break;
  293. case 'gc_probability':
  294. // do we have a path?
  295. if ( ! is_numeric($item) or $item < 0 or $item > 100)
  296. {
  297. // default value: 5%
  298. $item = 5;
  299. }
  300. break;
  301. default:
  302. // no config item for this driver
  303. break;
  304. }
  305. // global config, was validated in the driver
  306. $validated[$name] = $item;
  307. }
  308. }
  309. // validate all global settings as well
  310. return parent::_validate_config($validated);
  311. }
  312. }