StartSession.php 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. namespace Illuminate\Session\Middleware;
  3. use Closure;
  4. use Illuminate\Http\Request;
  5. use Illuminate\Support\Carbon;
  6. use Illuminate\Session\SessionManager;
  7. use Illuminate\Contracts\Session\Session;
  8. use Illuminate\Session\CookieSessionHandler;
  9. use Symfony\Component\HttpFoundation\Cookie;
  10. use Symfony\Component\HttpFoundation\Response;
  11. class StartSession
  12. {
  13. /**
  14. * The session manager.
  15. *
  16. * @var \Illuminate\Session\SessionManager
  17. */
  18. protected $manager;
  19. /**
  20. * Indicates if the session was handled for the current request.
  21. *
  22. * @var bool
  23. */
  24. protected $sessionHandled = false;
  25. /**
  26. * Create a new session middleware.
  27. *
  28. * @param \Illuminate\Session\SessionManager $manager
  29. * @return void
  30. */
  31. public function __construct(SessionManager $manager)
  32. {
  33. $this->manager = $manager;
  34. }
  35. /**
  36. * Handle an incoming request.
  37. *
  38. * @param \Illuminate\Http\Request $request
  39. * @param \Closure $next
  40. * @return mixed
  41. */
  42. public function handle($request, Closure $next)
  43. {
  44. $this->sessionHandled = true;
  45. // If a session driver has been configured, we will need to start the session here
  46. // so that the data is ready for an application. Note that the Laravel sessions
  47. // do not make use of PHP "native" sessions in any way since they are crappy.
  48. if ($this->sessionConfigured()) {
  49. $request->setLaravelSession(
  50. $session = $this->startSession($request)
  51. );
  52. $this->collectGarbage($session);
  53. }
  54. $response = $next($request);
  55. // Again, if the session has been configured we will need to close out the session
  56. // so that the attributes may be persisted to some storage medium. We will also
  57. // add the session identifier cookie to the application response headers now.
  58. if ($this->sessionConfigured()) {
  59. $this->storeCurrentUrl($request, $session);
  60. $this->addCookieToResponse($response, $session);
  61. }
  62. return $response;
  63. }
  64. /**
  65. * Perform any final actions for the request lifecycle.
  66. *
  67. * @param \Illuminate\Http\Request $request
  68. * @param \Symfony\Component\HttpFoundation\Response $response
  69. * @return void
  70. */
  71. public function terminate($request, $response)
  72. {
  73. if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions()) {
  74. $this->manager->driver()->save();
  75. }
  76. }
  77. /**
  78. * Start the session for the given request.
  79. *
  80. * @param \Illuminate\Http\Request $request
  81. * @return \Illuminate\Contracts\Session\Session
  82. */
  83. protected function startSession(Request $request)
  84. {
  85. return tap($this->getSession($request), function ($session) use ($request) {
  86. $session->setRequestOnHandler($request);
  87. $session->start();
  88. });
  89. }
  90. /**
  91. * Get the session implementation from the manager.
  92. *
  93. * @param \Illuminate\Http\Request $request
  94. * @return \Illuminate\Contracts\Session\Session
  95. */
  96. public function getSession(Request $request)
  97. {
  98. return tap($this->manager->driver(), function ($session) use ($request) {
  99. $session->setId($request->cookies->get($session->getName()));
  100. });
  101. }
  102. /**
  103. * Remove the garbage from the session if necessary.
  104. *
  105. * @param \Illuminate\Contracts\Session\Session $session
  106. * @return void
  107. */
  108. protected function collectGarbage(Session $session)
  109. {
  110. $config = $this->manager->getSessionConfig();
  111. // Here we will see if this request hits the garbage collection lottery by hitting
  112. // the odds needed to perform garbage collection on any given request. If we do
  113. // hit it, we'll call this handler to let it delete all the expired sessions.
  114. if ($this->configHitsLottery($config)) {
  115. $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
  116. }
  117. }
  118. /**
  119. * Determine if the configuration odds hit the lottery.
  120. *
  121. * @param array $config
  122. * @return bool
  123. */
  124. protected function configHitsLottery(array $config)
  125. {
  126. return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
  127. }
  128. /**
  129. * Store the current URL for the request if necessary.
  130. *
  131. * @param \Illuminate\Http\Request $request
  132. * @param \Illuminate\Contracts\Session\Session $session
  133. * @return void
  134. */
  135. protected function storeCurrentUrl(Request $request, $session)
  136. {
  137. if ($request->method() === 'GET' && $request->route() && ! $request->ajax()) {
  138. $session->setPreviousUrl($request->fullUrl());
  139. }
  140. }
  141. /**
  142. * Add the session cookie to the application response.
  143. *
  144. * @param \Symfony\Component\HttpFoundation\Response $response
  145. * @param \Illuminate\Contracts\Session\Session $session
  146. * @return void
  147. */
  148. protected function addCookieToResponse(Response $response, Session $session)
  149. {
  150. if ($this->usingCookieSessions()) {
  151. $this->manager->driver()->save();
  152. }
  153. if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
  154. $response->headers->setCookie(new Cookie(
  155. $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
  156. $config['path'], $config['domain'], $config['secure'] ?? false,
  157. $config['http_only'] ?? true, false, $config['same_site'] ?? null
  158. ));
  159. }
  160. }
  161. /**
  162. * Get the session lifetime in seconds.
  163. *
  164. * @return int
  165. */
  166. protected function getSessionLifetimeInSeconds()
  167. {
  168. return ($this->manager->getSessionConfig()['lifetime'] ?? null) * 60;
  169. }
  170. /**
  171. * Get the cookie lifetime in seconds.
  172. *
  173. * @return \DateTimeInterface
  174. */
  175. protected function getCookieExpirationDate()
  176. {
  177. $config = $this->manager->getSessionConfig();
  178. return $config['expire_on_close'] ? 0 : Carbon::now()->addMinutes($config['lifetime']);
  179. }
  180. /**
  181. * Determine if a session driver has been configured.
  182. *
  183. * @return bool
  184. */
  185. protected function sessionConfigured()
  186. {
  187. return ! is_null($this->manager->getSessionConfig()['driver'] ?? null);
  188. }
  189. /**
  190. * Determine if the configured session driver is persistent.
  191. *
  192. * @param array|null $config
  193. * @return bool
  194. */
  195. protected function sessionIsPersistent(array $config = null)
  196. {
  197. $config = $config ?: $this->manager->getSessionConfig();
  198. return ! in_array($config['driver'], [null, 'array']);
  199. }
  200. /**
  201. * Determine if the session is using cookie sessions.
  202. *
  203. * @return bool
  204. */
  205. protected function usingCookieSessions()
  206. {
  207. if ($this->sessionConfigured()) {
  208. return $this->manager->driver()->getHandler() instanceof CookieSessionHandler;
  209. }
  210. return false;
  211. }
  212. }