PageRenderTime 54ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php

http://github.com/fabpot/symfony
PHP | 256 lines | 123 code | 35 blank | 98 comment | 5 complexity | c110c02c17ab517926d4b820f7d20181 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpFoundation\SessionStorage;
  11. /**
  12. * PdoSessionStorage.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. * @author Michael Williams <michael.williams@funsational.com>
  16. */
  17. class PdoSessionStorage extends NativeSessionStorage
  18. {
  19. private $db;
  20. private $dbOptions;
  21. /**
  22. * Constructor.
  23. *
  24. * @param \PDO $db A PDO instance
  25. * @param array $options An associative array of session options
  26. * @param array $dbOptions An associative array of DB options
  27. *
  28. * @throws \InvalidArgumentException When "db_table" option is not provided
  29. *
  30. * @see NativeSessionStorage::__construct()
  31. */
  32. public function __construct(\PDO $db, array $options = array(), array $dbOptions = array())
  33. {
  34. if (!array_key_exists('db_table', $dbOptions)) {
  35. throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
  36. }
  37. $this->db = $db;
  38. $this->dbOptions = array_merge(array(
  39. 'db_id_col' => 'sess_id',
  40. 'db_data_col' => 'sess_data',
  41. 'db_time_col' => 'sess_time',
  42. ), $dbOptions);
  43. parent::__construct($options);
  44. }
  45. /**
  46. * Starts the session.
  47. */
  48. public function start()
  49. {
  50. if (self::$sessionStarted) {
  51. return;
  52. }
  53. // use this object as the session handler
  54. session_set_save_handler(
  55. array($this, 'sessionOpen'),
  56. array($this, 'sessionClose'),
  57. array($this, 'sessionRead'),
  58. array($this, 'sessionWrite'),
  59. array($this, 'sessionDestroy'),
  60. array($this, 'sessionGC')
  61. );
  62. parent::start();
  63. }
  64. /**
  65. * Opens a session.
  66. *
  67. * @param string $path (ignored)
  68. * @param string $name (ignored)
  69. *
  70. * @return Boolean true, if the session was opened, otherwise an exception is thrown
  71. */
  72. public function sessionOpen($path = null, $name = null)
  73. {
  74. return true;
  75. }
  76. /**
  77. * Closes a session.
  78. *
  79. * @return Boolean true, if the session was closed, otherwise false
  80. */
  81. public function sessionClose()
  82. {
  83. // do nothing
  84. return true;
  85. }
  86. /**
  87. * Destroys a session.
  88. *
  89. * @param string $id A session ID
  90. *
  91. * @return Boolean true, if the session was destroyed, otherwise an exception is thrown
  92. *
  93. * @throws \RuntimeException If the session cannot be destroyed
  94. */
  95. public function sessionDestroy($id)
  96. {
  97. // get table/column
  98. $dbTable = $this->dbOptions['db_table'];
  99. $dbIdCol = $this->dbOptions['db_id_col'];
  100. // delete the record associated with this id
  101. $sql = 'DELETE FROM '.$dbTable.' WHERE '.$dbIdCol.'= ?';
  102. try {
  103. $stmt = $this->db->prepare($sql);
  104. $stmt->bindParam(1, $id, \PDO::PARAM_STR);
  105. $stmt->execute();
  106. } catch (\PDOException $e) {
  107. throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
  108. }
  109. return true;
  110. }
  111. /**
  112. * Cleans up old sessions.
  113. *
  114. * @param int $lifetime The lifetime of a session
  115. *
  116. * @return Boolean true, if old sessions have been cleaned, otherwise an exception is thrown
  117. *
  118. * @throws \RuntimeException If any old sessions cannot be cleaned
  119. */
  120. public function sessionGC($lifetime)
  121. {
  122. // get table/column
  123. $dbTable = $this->dbOptions['db_table'];
  124. $dbTimeCol = $this->dbOptions['db_time_col'];
  125. // delete the record associated with this id
  126. $sql = 'DELETE FROM '.$dbTable.' WHERE '.$dbTimeCol.' < '.(time() - $lifetime);
  127. try {
  128. $this->db->query($sql);
  129. } catch (\PDOException $e) {
  130. throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
  131. }
  132. return true;
  133. }
  134. /**
  135. * Reads a session.
  136. *
  137. * @param string $id A session ID
  138. *
  139. * @return string The session data if the session was read or created, otherwise an exception is thrown
  140. *
  141. * @throws \RuntimeException If the session cannot be read
  142. */
  143. public function sessionRead($id)
  144. {
  145. // get table/columns
  146. $dbTable = $this->dbOptions['db_table'];
  147. $dbDataCol = $this->dbOptions['db_data_col'];
  148. $dbIdCol = $this->dbOptions['db_id_col'];
  149. try {
  150. $sql = 'SELECT '.$dbDataCol.' FROM '.$dbTable.' WHERE '.$dbIdCol.'=?';
  151. $stmt = $this->db->prepare($sql);
  152. $stmt->bindParam(1, $id, \PDO::PARAM_STR, 255);
  153. $stmt->execute();
  154. // it is recommended to use fetchAll so that PDO can close the DB cursor
  155. // we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777
  156. $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
  157. if (count($sessionRows) == 1) {
  158. return $sessionRows[0][0];
  159. }
  160. // session does not exist, create it
  161. $this->createNewSession($id);
  162. return '';
  163. } catch (\PDOException $e) {
  164. throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
  165. }
  166. }
  167. /**
  168. * Writes session data.
  169. *
  170. * @param string $id A session ID
  171. * @param string $data A serialized chunk of session data
  172. *
  173. * @return Boolean true, if the session was written, otherwise an exception is thrown
  174. *
  175. * @throws \RuntimeException If the session data cannot be written
  176. */
  177. public function sessionWrite($id, $data)
  178. {
  179. // get table/column
  180. $dbTable = $this->dbOptions['db_table'];
  181. $dbDataCol = $this->dbOptions['db_data_col'];
  182. $dbIdCol = $this->dbOptions['db_id_col'];
  183. $dbTimeCol = $this->dbOptions['db_time_col'];
  184. $sql = 'UPDATE '.$dbTable.' SET '.$dbDataCol.' = ?, '.$dbTimeCol.' = '.time().' WHERE '.$dbIdCol.'= ?';
  185. try {
  186. $stmt = $this->db->prepare($sql);
  187. $stmt->bindParam(1, $data, \PDO::PARAM_STR);
  188. $stmt->bindParam(2, $id, \PDO::PARAM_STR);
  189. $stmt->execute();
  190. if (!$stmt->rowCount()) {
  191. // No session exists in the database to update. This happens when we have called
  192. // session_regenerate_id()
  193. $this->createNewSession($id, $data);
  194. }
  195. } catch (\PDOException $e) {
  196. throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
  197. }
  198. return true;
  199. }
  200. /**
  201. * Creates a new session with the given $id and $data
  202. *
  203. * @param string $id
  204. * @param string $data
  205. */
  206. private function createNewSession($id, $data = '')
  207. {
  208. // get table/column
  209. $dbTable = $this->dbOptions['db_table'];
  210. $dbDataCol = $this->dbOptions['db_data_col'];
  211. $dbIdCol = $this->dbOptions['db_id_col'];
  212. $dbTimeCol = $this->dbOptions['db_time_col'];
  213. $sql = 'INSERT INTO '.$dbTable.'('.$dbIdCol.', '.$dbDataCol.', '.$dbTimeCol.') VALUES (?, ?, ?)';
  214. $stmt = $this->db->prepare($sql);
  215. $stmt->bindParam(1, $id, \PDO::PARAM_STR);
  216. $stmt->bindValue(2, $data, \PDO::PARAM_STR);
  217. $stmt->bindValue(3, time(), \PDO::PARAM_INT);
  218. $stmt->execute();
  219. return true;
  220. }
  221. }