/backend/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php

https://gitlab.com/x33n/Backbone-Music-Store · PHP · 231 lines · 122 code · 32 blank · 77 comment · 6 complexity · 8f978fe44a1a5bbf9e82af6dda5a3945 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\Session\Storage\Handler;
  11. /**
  12. * PdoSessionHandler.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. * @author Michael Williams <michael.williams@funsational.com>
  16. */
  17. class PdoSessionHandler implements \SessionHandlerInterface
  18. {
  19. /**
  20. * @var \PDO PDO instance.
  21. */
  22. private $pdo;
  23. /**
  24. * @var array Database options.
  25. */
  26. private $dbOptions;
  27. /**
  28. * Constructor.
  29. *
  30. * List of available options:
  31. * * db_table: The name of the table [required]
  32. * * db_id_col: The column where to store the session id [default: sess_id]
  33. * * db_data_col: The column where to store the session data [default: sess_data]
  34. * * db_time_col: The column where to store the timestamp [default: sess_time]
  35. *
  36. * @param \PDO $pdo A \PDO instance
  37. * @param array $dbOptions An associative array of DB options
  38. *
  39. * @throws \InvalidArgumentException When "db_table" option is not provided
  40. */
  41. public function __construct(\PDO $pdo, array $dbOptions = array())
  42. {
  43. if (!array_key_exists('db_table', $dbOptions)) {
  44. throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
  45. }
  46. $this->pdo = $pdo;
  47. $this->dbOptions = array_merge(array(
  48. 'db_id_col' => 'sess_id',
  49. 'db_data_col' => 'sess_data',
  50. 'db_time_col' => 'sess_time',
  51. ), $dbOptions);
  52. }
  53. /**
  54. * {@inheritDoc}
  55. */
  56. public function open($path, $name)
  57. {
  58. return true;
  59. }
  60. /**
  61. * {@inheritDoc}
  62. */
  63. public function close()
  64. {
  65. return true;
  66. }
  67. /**
  68. * {@inheritDoc}
  69. */
  70. public function destroy($id)
  71. {
  72. // get table/column
  73. $dbTable = $this->dbOptions['db_table'];
  74. $dbIdCol = $this->dbOptions['db_id_col'];
  75. // delete the record associated with this id
  76. $sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id";
  77. try {
  78. $stmt = $this->pdo->prepare($sql);
  79. $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
  80. $stmt->execute();
  81. } catch (\PDOException $e) {
  82. throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
  83. }
  84. return true;
  85. }
  86. /**
  87. * {@inheritDoc}
  88. */
  89. public function gc($lifetime)
  90. {
  91. // get table/column
  92. $dbTable = $this->dbOptions['db_table'];
  93. $dbTimeCol = $this->dbOptions['db_time_col'];
  94. // delete the session records that have expired
  95. $sql = "DELETE FROM $dbTable WHERE $dbTimeCol < :time";
  96. try {
  97. $stmt = $this->pdo->prepare($sql);
  98. $stmt->bindValue(':time', time() - $lifetime, \PDO::PARAM_INT);
  99. $stmt->execute();
  100. } catch (\PDOException $e) {
  101. throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
  102. }
  103. return true;
  104. }
  105. /**
  106. * {@inheritDoc}
  107. */
  108. public function read($id)
  109. {
  110. // get table/columns
  111. $dbTable = $this->dbOptions['db_table'];
  112. $dbDataCol = $this->dbOptions['db_data_col'];
  113. $dbIdCol = $this->dbOptions['db_id_col'];
  114. try {
  115. $sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id";
  116. $stmt = $this->pdo->prepare($sql);
  117. $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
  118. $stmt->execute();
  119. // it is recommended to use fetchAll so that PDO can close the DB cursor
  120. // we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777
  121. $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
  122. if (count($sessionRows) == 1) {
  123. return base64_decode($sessionRows[0][0]);
  124. }
  125. // session does not exist, create it
  126. $this->createNewSession($id);
  127. return '';
  128. } catch (\PDOException $e) {
  129. throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
  130. }
  131. }
  132. /**
  133. * {@inheritDoc}
  134. */
  135. public function write($id, $data)
  136. {
  137. // get table/column
  138. $dbTable = $this->dbOptions['db_table'];
  139. $dbDataCol = $this->dbOptions['db_data_col'];
  140. $dbIdCol = $this->dbOptions['db_id_col'];
  141. $dbTimeCol = $this->dbOptions['db_time_col'];
  142. //session data can contain non binary safe characters so we need to encode it
  143. $encoded = base64_encode($data);
  144. try {
  145. if ('mysql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
  146. // MySQL would report $stmt->rowCount() = 0 on UPDATE when the data is left unchanged
  147. // it could result in calling createNewSession() whereas the session already exists in
  148. // the DB which would fail as the id is unique
  149. $stmt = $this->pdo->prepare(
  150. "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) " .
  151. "ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = VALUES($dbTimeCol)"
  152. );
  153. $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
  154. $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
  155. $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
  156. $stmt->execute();
  157. } else {
  158. $stmt = $this->pdo->prepare("UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id");
  159. $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
  160. $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
  161. $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
  162. $stmt->execute();
  163. if (!$stmt->rowCount()) {
  164. // No session exists in the database to update. This happens when we have called
  165. // session_regenerate_id()
  166. $this->createNewSession($id, $data);
  167. }
  168. }
  169. } catch (\PDOException $e) {
  170. throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
  171. }
  172. return true;
  173. }
  174. /**
  175. * Creates a new session with the given $id and $data
  176. *
  177. * @param string $id
  178. * @param string $data
  179. *
  180. * @return boolean True.
  181. */
  182. private function createNewSession($id, $data = '')
  183. {
  184. // get table/column
  185. $dbTable = $this->dbOptions['db_table'];
  186. $dbDataCol = $this->dbOptions['db_data_col'];
  187. $dbIdCol = $this->dbOptions['db_id_col'];
  188. $dbTimeCol = $this->dbOptions['db_time_col'];
  189. $sql = "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time)";
  190. //session data can contain non binary safe characters so we need to encode it
  191. $encoded = base64_encode($data);
  192. $stmt = $this->pdo->prepare($sql);
  193. $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
  194. $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
  195. $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
  196. $stmt->execute();
  197. return true;
  198. }
  199. }