PageRenderTime 60ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Phoriz/Session/FileSession/FileSessionHandler.php

https://bitbucket.org/kasparp/phoriz
PHP | 285 lines | 169 code | 21 blank | 95 comment | 22 complexity | 24bfa7d055ea8ada49e517d54a3c88d6 MD5 | raw file
Possible License(s): EPL-1.0
  1. <?php
  2. /******************************************************************************
  3. * Copyright (c) 29/01/13 Kaspar Bach Pedersen.
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution, and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Kaspar Bach Pedersen - initial API and implementation and/or initial
  11. * documentation
  12. ******************************************************************************/
  13. namespace Phoriz\Session\FileSession;
  14. use DI\Annotation\Inject;
  15. use Phoriz\Exceptions\SessionHandlerException;
  16. use Phoriz\Session\SessionHandler;
  17. class FileSessionHandler extends SessionHandler
  18. {
  19. /**
  20. * @Inject("session.params")
  21. */
  22. private $configuration;
  23. /**
  24. * @var \string
  25. */
  26. private $sessionId;
  27. /**
  28. * @var \resource[]
  29. */
  30. private $files = array();
  31. /**
  32. * Open the session
  33. */
  34. public function open($sessionId)
  35. {
  36. $this->sessionId = $sessionId;
  37. // Check session age and delete if older than max age
  38. $maxAge = $this->configuration['lifetime_s'];
  39. $sessionAge = $this->findSessionAge($sessionId);
  40. if ($sessionAge !== false && $sessionAge >= $maxAge) {
  41. $this->destroy();
  42. }
  43. }
  44. /**
  45. * Close the session
  46. */
  47. public function close()
  48. {
  49. foreach ($this->files as $handle) {
  50. @flock($handle, LOCK_UN);
  51. @fclose($handle);
  52. }
  53. $this->files = array();
  54. }
  55. /**
  56. * Destroy the session
  57. */
  58. public function destroy()
  59. {
  60. $this->close();
  61. $path = $this->configuration['store_path'];
  62. $globvar = $path . DIRECTORY_SEPARATOR . $this->sessionId . '-*.session';
  63. $files = glob($globvar);
  64. foreach ($files as $file) {
  65. @unlink($file);
  66. }
  67. $this->files = array();
  68. }
  69. /**
  70. * Garbage collect expired sessions.
  71. */
  72. public function gc()
  73. {
  74. $path = $this->configuration['store_path'];
  75. $maxAge = $this->configuration['lifetime_s'];
  76. $sessionIds = $this->listActiveSessions($path);
  77. foreach ($sessionIds as $currentSessionId) {
  78. $globName = $path . DIRECTORY_SEPARATOR . $currentSessionId . '-*.' . 'session';
  79. $currFiles = glob($globName);
  80. $currAge = $this->findAgeOfNewestFile($currFiles);
  81. if ($currAge !== false && $currAge >= $maxAge) {
  82. foreach ($currFiles as $file) {
  83. @unlink($file);
  84. }
  85. }
  86. }
  87. }
  88. /**
  89. * Find the last accessed file and return age
  90. * @param \string[] $files
  91. * @return int age in seconds
  92. */
  93. private function findAgeOfNewestFile($files)
  94. {
  95. $now = time();
  96. $newestAge = false;
  97. foreach ($files as $file) {
  98. $filetime = fileatime($file);
  99. if ($filetime !== false) {
  100. $currentAge = $now - $filetime;
  101. if ($newestAge === false || $currentAge < $newestAge) {
  102. $newestAge = $currentAge;
  103. }
  104. }
  105. }
  106. return $newestAge;
  107. }
  108. /**
  109. * Find age of given session
  110. * @param string $sessionId
  111. * @return int age in seconds
  112. */
  113. private function findSessionAge($sessionId)
  114. {
  115. $path = $this->configuration['store_path'];
  116. $globName = $path . DIRECTORY_SEPARATOR . $sessionId . '-*.' . 'session';
  117. $files = glob($globName);
  118. return $this->findAgeOfNewestFile($files);
  119. }
  120. /**
  121. * List all sessions in path
  122. * @param $path
  123. * @return array with session id's
  124. */
  125. private function listActiveSessions($path)
  126. {
  127. $sessionIds = array();
  128. $globvar = $path . DIRECTORY_SEPARATOR . '*.session';
  129. $files = glob($globvar);
  130. foreach ($files as $file) {
  131. if (is_file($file)) {
  132. $exploded = explode('-', basename($file));
  133. if (count($exploded) == 2) {
  134. $sessionId = $exploded[0];
  135. if (!in_array($sessionId, $sessionIds)) {
  136. $sessionIds[] = $sessionId;
  137. }
  138. }
  139. }
  140. }
  141. return $sessionIds;
  142. }
  143. /**
  144. * Read a session variable.
  145. * If the value should be modified use readAndLock instead.
  146. * @param string $sessionVar name of session variable
  147. * @param bool $lock lock this session variable (use full if multiple requests write to the samme session var)
  148. * @param null $resolver
  149. * @return mixed|null content of the read session var
  150. */
  151. public function read($sessionVar, $lock = false, $resolver = null)
  152. {
  153. $filename = $this->varFilename($sessionVar);
  154. if (!isset($this->files[$sessionVar])|| !is_file($filename)) {
  155. $this->files[$sessionVar] = fopen($filename, 'c+');
  156. if ($lock) {
  157. flock($this->files[$sessionVar], LOCK_EX);
  158. }
  159. }
  160. return $this->seekAndRead($sessionVar, $filename);
  161. }
  162. /**
  163. * Write a session variable.
  164. * @param string $sessionVar name of session variable
  165. * @param mixed $sessionValue content of session var
  166. * @param bool $unlock unlock the session variable when the variable has been written
  167. * @param null|SessionContainerResolver $resolver
  168. * @return void
  169. */
  170. public function write($sessionVar, $sessionValue, $unlock = false, $resolver = null)
  171. {
  172. if (isset($this->files[$sessionVar])) {
  173. $value = serialize($sessionValue);
  174. $handle = $this->files[$sessionVar];
  175. $this->perfromWrite($this->varFilename($sessionVar), $handle, $value);
  176. if ($unlock) {
  177. flock($this->files[$sessionVar], LOCK_UN);
  178. }
  179. }
  180. }
  181. /**
  182. * Removes a session variable from current session
  183. * @param string $sessionVar
  184. * @return null
  185. */
  186. public function removeVar($sessionVar)
  187. {
  188. if (isset($this->files[$sessionVar])) {
  189. flock($this->files[$sessionVar], LOCK_UN);
  190. fclose($this->files[$sessionVar]);
  191. unset($this->files[$sessionVar]);
  192. }
  193. $filename = $this->varFilename($sessionVar);
  194. if (is_file($filename)) {
  195. @unlink($filename);
  196. }
  197. }
  198. /**
  199. * Check if an session var exists
  200. * @param string $sessionVar
  201. * @return boolean
  202. */
  203. public function varExists($sessionVar)
  204. {
  205. return is_file($this->varFilename($sessionVar));
  206. }
  207. /**
  208. * Calculates filename of a session var
  209. * @param \string $sessionVar session var name
  210. * @return string
  211. */
  212. private function varFilename($sessionVar)
  213. {
  214. $path = $this->configuration['store_path'];
  215. $filename = $path . DIRECTORY_SEPARATOR . $this->sessionId . '-' . $sessionVar . '.' . 'session';
  216. return $filename;
  217. }
  218. /**
  219. * @param $sessionVar
  220. * @param \string $filename
  221. * @return mixed|null
  222. * @throws SessionHandlerException
  223. */
  224. public function seekAndRead($sessionVar, $filename)
  225. {
  226. fseek($this->files[$sessionVar], SEEK_SET, 0);
  227. if (filesize($filename) > 0) {
  228. $fread = fread($this->files[$sessionVar], filesize($filename));
  229. if ($fread === false) {
  230. throw new SessionHandlerException("Failed to read session var from ".$filename);
  231. }
  232. return unserialize($fread);
  233. } else {
  234. return null;
  235. }
  236. }
  237. /**
  238. * @param $filename
  239. * @param \resource $handle
  240. * @param string $value
  241. * @throws SessionHandlerException
  242. * @return void
  243. */
  244. private function perfromWrite($filename, $handle, $value)
  245. {
  246. $length = strlen($value);
  247. fseek($handle, SEEK_SET, 0);
  248. while ($length > 0) {
  249. $res = fwrite($handle, $value);
  250. if ($res === false) {
  251. throw new SessionHandlerException("Failed to write session var to ".$filename);
  252. }
  253. $length -= $res;
  254. }
  255. ftruncate($handle, strlen($value));
  256. }
  257. /** Does this session handler support locking
  258. * @return bool
  259. */
  260. public function supportsLocking()
  261. {
  262. return true;
  263. }
  264. }