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

/classes/Session.php

http://github.com/joshtronic/pickles
PHP | 357 lines | 144 code | 41 blank | 172 comment | 24 complexity | 3f53be0a7fe4fbdfb896bdeb0a3e4537 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Session Handling for PICKLES
  4. *
  5. * PHP version 5
  6. *
  7. * Licensed under The MIT License
  8. * Redistribution of these files must retain the above copyright notice.
  9. *
  10. * @author Josh Sherman <josh@gravityblvd.com>
  11. * @copyright Copyright 2007-2011, Josh Sherman
  12. * @license http://www.opensource.org/licenses/mit-license.html
  13. * @package PICKLES
  14. * @link http://p.ickl.es
  15. */
  16. /**
  17. * Session Class
  18. *
  19. * Provides session handling via database instead of the file based session
  20. * handling built into PHP. Using this class requires an array to be defined
  21. * in place of the boolean true/false (on/off). If simply array(), the
  22. * datasource will default to the value in $config['pickles']['datasource'] and
  23. * if the table will default to "sessions". The format is as follows:
  24. *
  25. * $config = array(
  26. * 'pickles' => array(
  27. * 'session' => array(
  28. * 'datasource' => 'mysql',
  29. * 'table' => 'sessions',
  30. * )
  31. * )
  32. * );
  33. *
  34. * In addition to the configuration variables, a table in your database must
  35. * be created. The [MySQL] table schema is as follows:
  36. *
  37. * CREATE TABLE sessions (
  38. * id varchar(32) COLLATE utf8_unicode_ci NOT NULL,
  39. * session text COLLATE utf8_unicode_ci NOT NULL,
  40. * expires_at datetime NOT NULL,
  41. * PRIMARY KEY (id)
  42. * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  43. *
  44. * Note: The reason for not using a model class was to avoid a naming conflict
  45. * between the Session model and the Session class itself. This will eventually
  46. * be resolved when I abandon full 5.x support and migrate to 5.3+ (assuming
  47. * that ever happens).
  48. */
  49. class Session extends Object
  50. {
  51. /**
  52. * Handler
  53. *
  54. * What the session is being handled by.
  55. *
  56. * @access private
  57. * @var string
  58. */
  59. private $handler = false;
  60. /**
  61. * Accessed At
  62. *
  63. * The UNIX timestamp of when the page was accessed.
  64. *
  65. * @access private
  66. * @var integer
  67. */
  68. private $accessed_at = null;
  69. /**
  70. * Time to Live
  71. *
  72. * The number of seconds the session should remain active. Corresponds to
  73. * the INI variable session.gc_maxlifetime
  74. *
  75. * @access private
  76. * @var integer
  77. */
  78. private $time_to_live = null;
  79. /**
  80. * Datasource
  81. *
  82. * Name of the datasource, defaults to whatever the default datasource
  83. * is defined to in config.php
  84. *
  85. * @access private
  86. * @var string
  87. */
  88. private $datasource = null;
  89. /**
  90. * Table
  91. *
  92. * Name of the database table in the aforementioned datasource that holds
  93. * the session data. The expected schema is defined above.
  94. *
  95. * @access private
  96. * @var string
  97. */
  98. private $table = null;
  99. /**
  100. * Database
  101. *
  102. * Our database object to interact with the aforementioned datasource and
  103. * table. This object is shared with other PICKLES internals.
  104. *
  105. * @access private
  106. * @var object
  107. */
  108. private $db = null;
  109. /**
  110. * Constructor
  111. *
  112. * All of our set up logic for the session in contained here. This object
  113. * is initially instantiated from pickles.php and the session callbacks are
  114. * established here. All variables are driven from php.ini and/or the site
  115. * config. Once configured, the session is started automatically.
  116. */
  117. public function __construct()
  118. {
  119. if (!IS_CLI && (isset($_REQUEST['request']) == false || preg_match('/^__pickles\/(css|js)\/.+$/', $_REQUEST['request']) == false))
  120. {
  121. parent::__construct();
  122. // Sets up our configuration variables
  123. $session = $this->config->pickles['session'];
  124. $datasources = $this->config->datasources;
  125. $datasource = false;
  126. $table = 'sessions';
  127. if (is_array($session))
  128. {
  129. if (isset($session['handler']) && in_array($session['handler'], array('files', 'memcache', 'mysql')))
  130. {
  131. $this->handler = $session['handler'];
  132. if ($this->handler != 'files')
  133. {
  134. if (isset($session['datasource']))
  135. {
  136. $datasource = $session['datasource'];
  137. }
  138. if (isset($session['table']))
  139. {
  140. $table = $session['table'];
  141. }
  142. }
  143. }
  144. }
  145. else
  146. {
  147. if ($session === true || $session == 'files')
  148. {
  149. $this->handler = 'files';
  150. }
  151. elseif ($session == 'memcache')
  152. {
  153. $this->handler = 'memcache';
  154. $datasource = 'memcached';
  155. }
  156. elseif ($session == 'mysql')
  157. {
  158. $this->handler = 'mysql';
  159. $datasource = 'mysql';
  160. }
  161. }
  162. switch ($this->handler)
  163. {
  164. case 'files':
  165. ini_set('session.save_handler', 'files');
  166. session_start();
  167. break;
  168. case 'memcache':
  169. $hostname = 'localhost';
  170. $port = 11211;
  171. if ($datasource !== false && isset($datasources[$datasource]))
  172. {
  173. $hostname = $datasources[$datasource]['hostname'];
  174. $port = $datasources[$datasource]['port'];
  175. }
  176. ini_set('session.save_handler', 'memcache');
  177. ini_set('session.save_path', 'tcp://' . $hostname . ':' . $port . '?persistent=1&amp;weight=1&amp;timeout=1&amp;retry_interval=15');
  178. session_start();
  179. break;
  180. case 'mysql':
  181. if ($datasource !== false && isset($datasources[$datasource]))
  182. {
  183. // Sets our access time and time to live
  184. $this->accessed_at = time();
  185. $this->time_to_live = ini_get('session.gc_maxlifetime');
  186. $this->datasource = $datasource;
  187. $this->table = $table;
  188. // Gets a database instance
  189. $this->db = Database::getInstance($this->datasource);
  190. // Initializes the session
  191. $this->initialize();
  192. session_start();
  193. }
  194. else
  195. {
  196. throw new Exception('Unable to determine which datasource to use');
  197. }
  198. break;
  199. }
  200. }
  201. }
  202. /**
  203. * Destructor
  204. *
  205. * Runs garbage collection and closes the session. I'm not sure if the
  206. * garbage collection should stay as it could be accomplished via php.ini
  207. * variables. The session_write_close() is present to combat a chicken
  208. * and egg scenario in earlier versions of PHP 5.
  209. */
  210. public function __destruct()
  211. {
  212. if ($this->handler == 'mysql')
  213. {
  214. $this->gc($this->time_to_live);
  215. session_write_close();
  216. }
  217. }
  218. /**
  219. * Initializes the Session
  220. *
  221. * This method exists to combat the fact that calling session_destroy()
  222. * also clears out the save handler. Upon destorying a session this method
  223. * is called again so the save handler is all set.
  224. */
  225. public function initialize()
  226. {
  227. // Sets up the session handler
  228. session_set_save_handler(
  229. array($this, 'open'),
  230. array($this, 'close'),
  231. array($this, 'read'),
  232. array($this, 'write'),
  233. array($this, 'destroy'),
  234. array($this, 'gc')
  235. );
  236. register_shutdown_function('session_write_close');
  237. }
  238. /**
  239. * Opens the Session
  240. *
  241. * Since the session is in the database, opens the database connection.
  242. * This step isn't really necessary as the Database object is smart enough
  243. * to open itself up upon execute.
  244. */
  245. public function open()
  246. {
  247. session_regenerate_id();
  248. return $this->db->open();
  249. }
  250. /**
  251. * Closes the Session
  252. *
  253. * Same as above, but in reverse.
  254. */
  255. public function close()
  256. {
  257. return $this->db->close();
  258. }
  259. /**
  260. * Reads the Session
  261. *
  262. * Checks the database for the session ID and returns the session data.
  263. *
  264. * @param string $id session ID
  265. * @return string serialized session data
  266. */
  267. public function read($id)
  268. {
  269. $sql = 'SELECT session FROM `' . $this->table . '` WHERE id = ?;';
  270. $session = $this->db->fetch($sql, array($id));
  271. return isset($session[0]['session']) ? $session[0]['session'] : '';
  272. }
  273. /**
  274. * Writes the Session
  275. *
  276. * When there's changes to the session, writes the data to the database.
  277. *
  278. * @param string $id session ID
  279. * @param string $session serialized session data
  280. * @return boolean whether the query executed correctly
  281. */
  282. public function write($id, $session)
  283. {
  284. $sql = 'REPLACE INTO `' . $this->table . '` VALUES (?, ? ,?);';
  285. $parameters = array($id, $session, date('Y-m-d H:i:s', strtotime('+' . $this->time_to_live . ' seconds')));
  286. return $this->db->execute($sql, $parameters);
  287. }
  288. /**
  289. * Destroys the Session
  290. *
  291. * Deletes the session from the database.
  292. *
  293. * @param string $id session ID
  294. * @return boolean whether the query executed correctly
  295. */
  296. public function destroy($id)
  297. {
  298. $sql = 'DELETE FROM `' . $this->table . '` WHERE id = ?;';
  299. return $this->db->execute($sql, array($id));
  300. }
  301. /**
  302. * Garbage Collector
  303. *
  304. * This is who you call when you got trash to be taken out.
  305. *
  306. * @param integer $time_to_live number of seconds a session is active
  307. * @return boolean whether the query executed correctly
  308. */
  309. public function gc($time_to_live)
  310. {
  311. $sql = 'DELETE FROM `' . $this->table . '` WHERE expires_at < ?;';
  312. $parameters = array(date('Y-m-d H:i:s', $this->accessed_at - $time_to_live));
  313. return $this->db->execute($sql, $parameters);
  314. }
  315. }
  316. ?>