DatabaseSessionHandler.php 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <?php
  2. namespace Illuminate\Session;
  3. use Illuminate\Support\Arr;
  4. use SessionHandlerInterface;
  5. use Illuminate\Support\Carbon;
  6. use Illuminate\Contracts\Auth\Guard;
  7. use Illuminate\Database\QueryException;
  8. use Illuminate\Support\InteractsWithTime;
  9. use Illuminate\Database\ConnectionInterface;
  10. use Illuminate\Contracts\Container\Container;
  11. class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareInterface
  12. {
  13. use InteractsWithTime;
  14. /**
  15. * The database connection instance.
  16. *
  17. * @var \Illuminate\Database\ConnectionInterface
  18. */
  19. protected $connection;
  20. /**
  21. * The name of the session table.
  22. *
  23. * @var string
  24. */
  25. protected $table;
  26. /**
  27. * The number of minutes the session should be valid.
  28. *
  29. * @var int
  30. */
  31. protected $minutes;
  32. /**
  33. * The container instance.
  34. *
  35. * @var \Illuminate\Contracts\Container\Container
  36. */
  37. protected $container;
  38. /**
  39. * The existence state of the session.
  40. *
  41. * @var bool
  42. */
  43. protected $exists;
  44. /**
  45. * Create a new database session handler instance.
  46. *
  47. * @param \Illuminate\Database\ConnectionInterface $connection
  48. * @param string $table
  49. * @param int $minutes
  50. * @param \Illuminate\Contracts\Container\Container|null $container
  51. * @return void
  52. */
  53. public function __construct(ConnectionInterface $connection, $table, $minutes, Container $container = null)
  54. {
  55. $this->table = $table;
  56. $this->minutes = $minutes;
  57. $this->container = $container;
  58. $this->connection = $connection;
  59. }
  60. /**
  61. * {@inheritdoc}
  62. */
  63. public function open($savePath, $sessionName)
  64. {
  65. return true;
  66. }
  67. /**
  68. * {@inheritdoc}
  69. */
  70. public function close()
  71. {
  72. return true;
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. public function read($sessionId)
  78. {
  79. $session = (object) $this->getQuery()->find($sessionId);
  80. if ($this->expired($session)) {
  81. $this->exists = true;
  82. return '';
  83. }
  84. if (isset($session->payload)) {
  85. $this->exists = true;
  86. return base64_decode($session->payload);
  87. }
  88. return '';
  89. }
  90. /**
  91. * Determine if the session is expired.
  92. *
  93. * @param \stdClass $session
  94. * @return bool
  95. */
  96. protected function expired($session)
  97. {
  98. return isset($session->last_activity) &&
  99. $session->last_activity < Carbon::now()->subMinutes($this->minutes)->getTimestamp();
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function write($sessionId, $data)
  105. {
  106. $payload = $this->getDefaultPayload($data);
  107. if (! $this->exists) {
  108. $this->read($sessionId);
  109. }
  110. if ($this->exists) {
  111. $this->performUpdate($sessionId, $payload);
  112. } else {
  113. $this->performInsert($sessionId, $payload);
  114. }
  115. return $this->exists = true;
  116. }
  117. /**
  118. * Perform an insert operation on the session ID.
  119. *
  120. * @param string $sessionId
  121. * @param string $payload
  122. * @return bool|null
  123. */
  124. protected function performInsert($sessionId, $payload)
  125. {
  126. try {
  127. return $this->getQuery()->insert(Arr::set($payload, 'id', $sessionId));
  128. } catch (QueryException $e) {
  129. $this->performUpdate($sessionId, $payload);
  130. }
  131. }
  132. /**
  133. * Perform an update operation on the session ID.
  134. *
  135. * @param string $sessionId
  136. * @param string $payload
  137. * @return int
  138. */
  139. protected function performUpdate($sessionId, $payload)
  140. {
  141. return $this->getQuery()->where('id', $sessionId)->update($payload);
  142. }
  143. /**
  144. * Get the default payload for the session.
  145. *
  146. * @param string $data
  147. * @return array
  148. */
  149. protected function getDefaultPayload($data)
  150. {
  151. $payload = [
  152. 'payload' => base64_encode($data),
  153. 'last_activity' => $this->currentTime(),
  154. ];
  155. if (! $this->container) {
  156. return $payload;
  157. }
  158. return tap($payload, function (&$payload) {
  159. $this->addUserInformation($payload)
  160. ->addRequestInformation($payload);
  161. });
  162. }
  163. /**
  164. * Add the user information to the session payload.
  165. *
  166. * @param array $payload
  167. * @return $this
  168. */
  169. protected function addUserInformation(&$payload)
  170. {
  171. if ($this->container->bound(Guard::class)) {
  172. $payload['user_id'] = $this->userId();
  173. }
  174. return $this;
  175. }
  176. /**
  177. * Get the currently authenticated user's ID.
  178. *
  179. * @return mixed
  180. */
  181. protected function userId()
  182. {
  183. return $this->container->make(Guard::class)->id();
  184. }
  185. /**
  186. * Add the request information to the session payload.
  187. *
  188. * @param array $payload
  189. * @return $this
  190. */
  191. protected function addRequestInformation(&$payload)
  192. {
  193. if ($this->container->bound('request')) {
  194. $payload = array_merge($payload, [
  195. 'ip_address' => $this->ipAddress(),
  196. 'user_agent' => $this->userAgent(),
  197. ]);
  198. }
  199. return $this;
  200. }
  201. /**
  202. * Get the IP address for the current request.
  203. *
  204. * @return string
  205. */
  206. protected function ipAddress()
  207. {
  208. return $this->container->make('request')->ip();
  209. }
  210. /**
  211. * Get the user agent for the current request.
  212. *
  213. * @return string
  214. */
  215. protected function userAgent()
  216. {
  217. return substr((string) $this->container->make('request')->header('User-Agent'), 0, 500);
  218. }
  219. /**
  220. * {@inheritdoc}
  221. */
  222. public function destroy($sessionId)
  223. {
  224. $this->getQuery()->where('id', $sessionId)->delete();
  225. return true;
  226. }
  227. /**
  228. * {@inheritdoc}
  229. */
  230. public function gc($lifetime)
  231. {
  232. $this->getQuery()->where('last_activity', '<=', $this->currentTime() - $lifetime)->delete();
  233. }
  234. /**
  235. * Get a fresh query builder instance for the table.
  236. *
  237. * @return \Illuminate\Database\Query\Builder
  238. */
  239. protected function getQuery()
  240. {
  241. return $this->connection->table($this->table);
  242. }
  243. /**
  244. * Set the existence state for the session.
  245. *
  246. * @param bool $value
  247. * @return $this
  248. */
  249. public function setExists($value)
  250. {
  251. $this->exists = $value;
  252. return $this;
  253. }
  254. }