PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/horde-3.3.13/lib/Horde/SessionHandler/memcache.php

#
PHP | 372 lines | 187 code | 50 blank | 135 comment | 38 complexity | 581076ac71e812c10d1a4e021548687c MD5 | raw file
Possible License(s): LGPL-2.0
  1. <?php
  2. require_once 'Horde/Memcache.php';
  3. /**
  4. * SessionHandler:: implementation for memcache.
  5. *
  6. * NOTE FOR WINDOWS USERS w/PHP 4: Due to limitations in PHP 4, you should not
  7. * use the memcache driver. Either upgrade to PHP 5 or use a different
  8. * session handler.
  9. *
  10. * Optional parameters:<pre>
  11. * 'persistent_driver' - (string) If set, uses this backend to store session
  12. * data persistently.
  13. * 'persistent_params' - (array) If using a persistent backend, the params
  14. * to use for the persistent backend.
  15. * 'track' - (boolean) Track active sessions?
  16. * 'track_lifetime' - (integer) The number of seconds after which tracked
  17. * sessions will be treated as expired.
  18. * </pre>
  19. *
  20. * $Horde: framework/SessionHandler/SessionHandler/memcache.php,v 1.1.2.13 2009/10/06 22:22:20 slusarz Exp $
  21. *
  22. * Copyright 2005-2009 The Horde Project (http://www.horde.org/)
  23. *
  24. * See the enclosed file COPYING for license information (LGPL). If you
  25. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  26. *
  27. * @author Rong-En Fan <rafan@infor.org>
  28. * @author Michael Slusarz <slusarz@curecanti.org>
  29. * @since Horde 3.1
  30. * @package Horde_SessionHandler
  31. */
  32. class SessionHandler_memcache extends SessionHandler {
  33. /**
  34. * Horde_Memcache object.
  35. *
  36. * @var Horde_Cache
  37. */
  38. var $_memcache;
  39. /**
  40. * Current session ID.
  41. *
  42. * @var string
  43. */
  44. var $_id;
  45. /**
  46. * Persistent backend driver.
  47. *
  48. * @var SessionHandler
  49. */
  50. var $_persistent;
  51. /**
  52. * Do read-only get?
  53. *
  54. * @var boolean
  55. */
  56. var $_readonly = false;
  57. /**
  58. * The ID used for session tracking.
  59. *
  60. * @var string
  61. */
  62. var $_trackID = 'horde_memcache_sessions_track';
  63. /**
  64. * Constructs a new Memcache SessionHandler object.
  65. *
  66. * @param array $params A hash containing connection parameters.
  67. */
  68. function SessionHandler_memcache($params = array())
  69. {
  70. if (!empty($params['persistent_driver'])) {
  71. $this->_persistent = &SessionHandler::singleton($params['persistent_driver'], empty($params['persistent_params']) ? null : $params['persistent_params']);
  72. if (is_a($this->_persistent, 'PEAR_Error')) {
  73. return PEAR::raiseError('Horde is unable to correctly start the persistent session handler.');
  74. }
  75. }
  76. parent::SessionHandler($params);
  77. // If using a persistent backend, don't track sessions in memcache
  78. if (isset($this->_persistent)) {
  79. $this->_params['track'] = false;
  80. }
  81. if (empty($this->_params['track_lifetime'])) {
  82. $this->_params['track_lifetime'] = ini_get('session.gc_maxlifetime');
  83. }
  84. }
  85. /**
  86. * Open the SessionHandler backend.
  87. *
  88. * @access private
  89. *
  90. * @param string $save_path The path to the session object.
  91. * @param string $session_name The name of the session.
  92. *
  93. * @return boolean True on success, false otherwise.
  94. */
  95. function _open($save_path = null, $session_name = null)
  96. {
  97. $this->_memcache = &Horde_Memcache::singleton();
  98. if (is_a($this->_memcache, 'PEAR_Error')) {
  99. return $this->_memcache;
  100. }
  101. if (isset($this->_persistent)) {
  102. $res = $this->_persistent->open($save_path, $session_name);
  103. if (is_a($res, 'PEAR_Error')) {
  104. return $res;
  105. }
  106. }
  107. // Garbage collection for session tracking information - run 0.1%
  108. // of all session accesses.
  109. if (!empty($this->_params['track']) && (rand(0, 999) == 0)) {
  110. register_shutdown_function(array(&$this, '_trackGC'));
  111. }
  112. return true;
  113. }
  114. /**
  115. * Close the SessionHandler backend.
  116. *
  117. * @access private
  118. *
  119. * @return boolean True on success, false otherwise.
  120. */
  121. function _close()
  122. {
  123. if (isset($this->_id)) {
  124. $this->_memcache->unlock($this->_id);
  125. }
  126. if (isset($this->_persistent)) {
  127. $this->_persistent->close();
  128. }
  129. return true;
  130. }
  131. /**
  132. * Read the data for a particular session identifier.
  133. *
  134. * @access private
  135. *
  136. * @param string $id The session identifier.
  137. *
  138. * @return string The session data.
  139. */
  140. function _read($id)
  141. {
  142. if (!$this->_readonly) {
  143. $this->_memcache->lock($id);
  144. }
  145. $result = $this->_memcache->get($id);
  146. if ($result === false) {
  147. if (!$this->_readonly) {
  148. $this->_memcache->unlock($id);
  149. }
  150. if (isset($this->_persistent)) {
  151. $result = $this->_persistent->read($id);
  152. }
  153. if ($result === false) {
  154. Horde::logMessage('Error retrieving session data (id = ' . $id . ')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
  155. return false;
  156. }
  157. $this->_persistent->write($id, $session_data);
  158. }
  159. if (!$this->_readonly) {
  160. $this->_id = $id;
  161. }
  162. Horde::logMessage('Read session data (id = ' . $id . ')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
  163. return $result;
  164. }
  165. /**
  166. * Write session data to the SessionHandler backend.
  167. *
  168. * @access private
  169. *
  170. * @param string $id The session identifier.
  171. * @param string $session_data The session data.
  172. *
  173. * @return boolean True on success, false otherwise.
  174. */
  175. function _write($id, $session_data)
  176. {
  177. if (!empty($this->_params['track'])) {
  178. // Do a replace - the only time it should fail is if we are
  179. // writing a session for the first time. If that is the case,
  180. // update the session tracker.
  181. $res = $this->_memcache->replace($id, $session_data);
  182. $track = !$res;
  183. } else {
  184. $res = $track = false;
  185. }
  186. if (!$res &&
  187. !$this->_memcache->set($id, $session_data)) {
  188. Horde::logMessage('Error writing session data (id = ' . $id . ')', __FILE__, __LINE__, PEAR_LOG_ERR);
  189. return false;
  190. }
  191. if (isset($this->_persistent)) {
  192. $result = $this->_persistent->write($id, $session_data);
  193. }
  194. if ($track) {
  195. $this->_memcache->lock($this->_trackID);
  196. $ids = $this->_memcache->get($this->_trackID);
  197. if ($ids === false) {
  198. $ids = array();
  199. }
  200. $ids[$id] = time();
  201. $this->_memcache->set($this->_trackID, $ids);
  202. $this->_memcache->unlock($this->_trackID);
  203. }
  204. Horde::logMessage('Wrote session data (id = ' . $id . ')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
  205. return true;
  206. }
  207. /**
  208. * Destroy the data for a particular session identifier.
  209. *
  210. * @param string $id The session identifier.
  211. *
  212. * @return boolean True on success, false otherwise.
  213. */
  214. function destroy($id)
  215. {
  216. $result = $this->_memcache->delete($id);
  217. $this->_memcache->unlock($id);
  218. if ($result === false) {
  219. Horde::logMessage('Failed to delete session (id = ' . $id . ')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
  220. return false;
  221. }
  222. if (isset($this->_persistent)) {
  223. $result = $this->_persistent->destroy($id);
  224. }
  225. if (!empty($this->_params['track'])) {
  226. $this->_memcache->lock($this->_trackID);
  227. $ids = $this->_memcache->get($this->_trackID);
  228. if ($ids !== false) {
  229. unset($ids[$id]);
  230. $this->_memcache->set($this->_trackID, $ids);
  231. }
  232. $this->_memcache->unlock($this->_trackID);
  233. }
  234. Horde::logMessage('Deleted session data (id = ' . $id . ')', __FILE__, __LINE__, PEAR_LOG_DEBUG);
  235. return true;
  236. }
  237. /**
  238. * Garbage collect stale sessions from the SessionHandler backend.
  239. *
  240. * @param integer $maxlifetime The maximum age of a session.
  241. *
  242. * @return boolean True on success, false otherwise.
  243. */
  244. function gc($maxlifetime = 300)
  245. {
  246. $result = true;
  247. if (isset($this->_persistent)) {
  248. $result = $this->_persistent->gc($maxlifetime);
  249. }
  250. // Memcache does its own garbage collection.
  251. return $result;
  252. }
  253. /**
  254. * Get a list of (possibly) valid session identifiers.
  255. *
  256. * @return array A list of session identifiers or PEAR_Error.
  257. */
  258. function getSessionIDs()
  259. {
  260. $err = null;
  261. if (isset($this->_persistent)) {
  262. return $this->_persistent->getSessionIDs();
  263. }
  264. if (!$this->_open()) {
  265. $err = PEAR::raiseError(_("Could not connect to memcache servers."));
  266. }
  267. if (is_null($err) && empty($this->_params['track'])) {
  268. $err = PEAR::raiseError(_("Memcache session tracking not enabled."));
  269. }
  270. if ($err) {
  271. if (isset($this->_persistent)) {
  272. return $this->_persistent->getSessionIDs();
  273. }
  274. return $err;
  275. }
  276. $this->_trackGC();
  277. $ids = $this->_memcache->get($this->_trackID);
  278. return ($ids === false) ? array() : array_keys($ids);
  279. }
  280. /**
  281. * Get session data read-only.
  282. *
  283. * @access private
  284. *
  285. * @param string $id The session identifier.
  286. *
  287. * @return string The session data.
  288. */
  289. function _readOnly($id)
  290. {
  291. $this->_readonly = true;
  292. $result = $this->_memcache->get($id);
  293. $this->_readonly = false;
  294. return $result;
  295. }
  296. /**
  297. * Do garbage collection for session tracking information.
  298. *
  299. * @access private
  300. */
  301. function _trackGC()
  302. {
  303. $this->_memcache->lock($this->_trackID);
  304. $ids = $this->_memcache->get($this->_trackID);
  305. if (empty($ids)) {
  306. $this->_memcache->unlock($this->_trackID);
  307. return;
  308. }
  309. $tstamp = time() - $this->_params['track_lifetime'];
  310. $alter = false;
  311. foreach ($ids as $key => $val) {
  312. if ($tstamp > $val) {
  313. unset($ids[$key]);
  314. $alter = true;
  315. }
  316. }
  317. if ($alter) {
  318. $this->_memcache->set($this->_trackID, $ids);
  319. }
  320. $this->_memcache->unlock($this->_trackID);
  321. }
  322. }