1. sfCacheSessionStorage.class.php
  2. /** * sfCacheSessionStorage manages session storage via a signed cookie and cache backend. * * This class stores the session data in via sfCache instance and with an id issued in a * signed cookie. Useful when you don't want to store the session. * * @package symfony * @subpackage storage * @author Dustin Whittle */
  3. class sfCacheSessionStorage extends sfStorage
  4. {
  5. protected
  6. $id = null,
  7. $context = null,
  8. $dispatcher = null,
  9. $request = null,
  10. $response = null,
  11. $cache = null,
  12. $data = array(),
  13. $dataChanged = false;
  14. /**
  15. * Initialize this Storage.
  16. *
  17. * @param array $options An associative array of initialization parameters.
  18. * session_name [required] name of session to use
  19. * session_cookie_path [required] cookie path
  20. * session_cookie_domain [required] cookie domain
  21. * session_cookie_lifetime [required] liftime of cookie
  22. * session_cookie_secure [required] send only if secure connection
  23. * session_cookie_http_only [required] accessible only via http protocol
  24. *
  25. * @return bool true, when initialization completes successfully.
  26. *
  27. * @throws <b>sfInitializationException</b> If an error occurs while initializing this Storage.
  28. */
  29. public function initialize($options = array())
  30. {
  31. // initialize parent
  32. parent::initialize(array_merge(array('session_name' => 'sfproject',
  33. 'session_cookie_lifetime' => '+30 days',
  34. 'session_cookie_path' => '/',
  35. 'session_cookie_domain' => null,
  36. 'session_cookie_secure' => false,
  37. 'session_cookie_http_only' => true,
  38. 'session_cookie_secret' => 'sf$ecret'), $options));
  39. // create cache instance
  40. if (isset($this->options['cache']) && $this->options['cache']['class'])
  41. {
  42. $this->cache = new $this->options['cache']['class'](is_array($this->options['cache']['param']) ? $this->options['cache']['param'] : array());
  43. }
  44. else
  45. {
  46. throw new InvalidArgumentException('sfCacheSessionStorage requires cache option.');
  47. }
  48. $this->context = sfContext::getInstance();
  49. $this->dispatcher = $this->context->getEventDispatcher();
  50. $this->request = $this->context->getRequest();
  51. $this->response = $this->context->getResponse();
  52. $cookie = $this->request->getCookie($this->options['session_name']);
  53. if(strpos($cookie, ':') !== false)
  54. {
  55. // split cookie data id:signature(id+secret)
  56. list($id, $signature) = explode(':', $cookie, 2);
  57. if($signature == sha1($id.':'.$this->options['session_cookie_secret']))
  58. {
  59. // cookie is valid
  60. $this->id = $id;
  61. }
  62. else
  63. {
  64. // cookie signature broken
  65. $this->id = null;
  66. }
  67. }
  68. else
  69. {
  70. // cookie format wrong
  71. $this->id = null;
  72. }
  73. if(empty($this->id))
  74. {
  75. $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'localhost';
  76. $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'ua';
  77. // generate new id based on random # / ip / user agent / secret
  78. $this->id = md5(rand(0, 999999).$ip.$ua.$this->options['session_cookie_secret']);
  79. if(sfConfig::get('sf_logging_enabled'))
  80. {
  81. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('New session created')));
  82. }
  83. // only send cookie when id is issued
  84. $this->response->setCookie($this->options['session_name'],
  85. $this->id.':'.sha1($this->id.':'.$this->options['session_cookie_secret']),
  86. $this->options['session_cookie_lifetime'],
  87. $this->options['session_cookie_path'],
  88. $this->options['session_cookie_domain'],
  89. $this->options['session_cookie_secure'],
  90. $this->options['session_cookie_http_only']);
  91. $this->data = array();
  92. }
  93. else
  94. {
  95. // load data from cache
  96. $this->data = $this->cache->get($this->id, array());
  97. if(sfConfig::get('sf_logging_enabled'))
  98. {
  99. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Restored previous session')));
  100. }
  101. }
  102. session_id($this->id);
  103. return true;
  104. }
  105. /**
  106. * Write data to this storage.
  107. *
  108. * The preferred format for a key is directory style so naming conflicts can be avoided.
  109. *
  110. * @param string $key A unique key identifying your data.
  111. * @param mixed $data Data associated with your key.
  112. *
  113. * @return void
  114. */
  115. public function write($key, $data)
  116. {
  117. $this->dataChanged = true;
  118. $this->data[$key] =& $data;
  119. }
  120. /**
  121. * Read data from this storage.
  122. *
  123. * The preferred format for a key is directory style so naming conflicts can be avoided.
  124. *
  125. * @param string $key A unique key identifying your data.
  126. *
  127. * @return mixed Data associated with the key.
  128. */
  129. public function read($key)
  130. {
  131. $retval = null;
  132. if (isset($this->data[$key]))
  133. {
  134. $retval =& $this->data[$key];
  135. }
  136. return $retval;
  137. }
  138. /**
  139. * Remove data from this storage.
  140. *
  141. * The preferred format for a key is directory style so naming conflicts can be avoided.
  142. *
  143. * @param string $key A unique key identifying your data.
  144. *
  145. * @return mixed Data associated with the key.
  146. */
  147. public function remove($key)
  148. {
  149. $retval = null;
  150. if (isset($this->data[$key]))
  151. {
  152. $this->dataChanged = true;
  153. $retval =& $this->data[$key];
  154. unset($this->data[$key]);
  155. }
  156. return $retval;
  157. }
  158. /**
  159. * Regenerates id that represents this storage.
  160. *
  161. * @param boolean $destroy Destroy session when regenerating?
  162. *
  163. * @return boolean True if session regenerated, false if error
  164. *
  165. * @throws <b>sfStorageException</b> If an error occurs while regenerating this storage
  166. */
  167. public function regenerate($destroy = false)
  168. {
  169. if($destroy)
  170. {
  171. $this->data = array();
  172. $this->cache->remove($this->id);
  173. }
  174. // generate session id
  175. $this->id = md5(rand(0, 999999).$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].$this->options['session_cookie_secret']);
  176. // save data to cache
  177. $this->cache->set($this->id, $this->data);
  178. // update session id in signed cookie
  179. $this->response->setCookie($this->options['session_name'],
  180. $this->id.':'.sha1($this->id.':'.$this->options['session_cookie_secret']),
  181. $this->options['session_cookie_lifetime'],
  182. $this->options['session_cookie_path'],
  183. $this->options['session_cookie_domain'],
  184. $this->options['session_cookie_secure'],
  185. $this->options['session_cookie_http_only']);
  186. session_id($this->id);
  187. return true;
  188. }
  189. /**
  190. * Expires the session storage instance.
  191. */
  192. public function expire()
  193. {
  194. // destroy data and regenerate id
  195. $this->regenerate(true);
  196. if(sfConfig::get('sf_logging_enabled'))
  197. {
  198. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('new session created due to expiraton')));
  199. }
  200. }
  201. /**
  202. * Executes the shutdown procedure.
  203. *
  204. * @throws <b>sfStorageException</b> If an error occurs while shutting down this storage
  205. */
  206. public function shutdown()
  207. {
  208. // only update cache if session has changed
  209. if($this->dataChanged === true)
  210. {
  211. $this->cache->set($this->id, $this->data);
  212. if(sfConfig::get('sf_logging_enabled'))
  213. {
  214. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Storing session to cache')));
  215. // $this->dispatcher->notify(new sfEvent($this, 'application.log', array(var_export($this->data, true))));
  216. }
  217. }
  218. }
  219. }

Debug toolbar