/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php

https://github.com/ladybirdweb/agorainvoicing · PHP · 291 lines · 135 code · 35 blank · 121 comment · 8 complexity · fba9f2d1c761c539afc8f6c2d18ad5fc MD5 · raw file

  1. <?php
  2. namespace Illuminate\Session\Middleware;
  3. use Closure;
  4. use Illuminate\Contracts\Session\Session;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Session\SessionManager;
  7. use Illuminate\Support\Carbon;
  8. use Illuminate\Support\Facades\Date;
  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. * The callback that can resolve an instance of the cache factory.
  21. *
  22. * @var callable|null
  23. */
  24. protected $cacheFactoryResolver;
  25. /**
  26. * Create a new session middleware.
  27. *
  28. * @param \Illuminate\Session\SessionManager $manager
  29. * @param callable|null $cacheFactoryResolver
  30. * @return void
  31. */
  32. public function __construct(SessionManager $manager, callable $cacheFactoryResolver = null)
  33. {
  34. $this->manager = $manager;
  35. $this->cacheFactoryResolver = $cacheFactoryResolver;
  36. }
  37. /**
  38. * Handle an incoming request.
  39. *
  40. * @param \Illuminate\Http\Request $request
  41. * @param \Closure $next
  42. * @return mixed
  43. */
  44. public function handle($request, Closure $next)
  45. {
  46. if (! $this->sessionConfigured()) {
  47. return $next($request);
  48. }
  49. $session = $this->getSession($request);
  50. if ($this->manager->shouldBlock() ||
  51. ($request->route() && $request->route()->locksFor())) {
  52. return $this->handleRequestWhileBlocking($request, $session, $next);
  53. } else {
  54. return $this->handleStatefulRequest($request, $session, $next);
  55. }
  56. }
  57. /**
  58. * Handle the given request within session state.
  59. *
  60. * @param \Illuminate\Http\Request $request
  61. * @param \Illuminate\Contracts\Session\Session $session
  62. * @param \Closure $next
  63. * @return mixed
  64. */
  65. protected function handleRequestWhileBlocking(Request $request, $session, Closure $next)
  66. {
  67. $lockFor = $request->route() && $request->route()->locksFor()
  68. ? $request->route()->locksFor()
  69. : 10;
  70. $lock = $this->cache($this->manager->blockDriver())
  71. ->lock('session:'.$session->getId(), $lockFor)
  72. ->betweenBlockedAttemptsSleepFor(50);
  73. try {
  74. $lock->block(
  75. ! is_null($request->route()->waitsFor())
  76. ? $request->route()->waitsFor()
  77. : 10
  78. );
  79. return $this->handleStatefulRequest($request, $session, $next);
  80. } finally {
  81. optional($lock)->release();
  82. }
  83. }
  84. /**
  85. * Handle the given request within session state.
  86. *
  87. * @param \Illuminate\Http\Request $request
  88. * @param \Illuminate\Contracts\Session\Session $session
  89. * @param \Closure $next
  90. * @return mixed
  91. */
  92. protected function handleStatefulRequest(Request $request, $session, Closure $next)
  93. {
  94. // If a session driver has been configured, we will need to start the session here
  95. // so that the data is ready for an application. Note that the Laravel sessions
  96. // do not make use of PHP "native" sessions in any way since they are crappy.
  97. $request->setLaravelSession(
  98. $this->startSession($request, $session)
  99. );
  100. $this->collectGarbage($session);
  101. $response = $next($request);
  102. $this->storeCurrentUrl($request, $session);
  103. $this->addCookieToResponse($response, $session);
  104. // Again, if the session has been configured we will need to close out the session
  105. // so that the attributes may be persisted to some storage medium. We will also
  106. // add the session identifier cookie to the application response headers now.
  107. $this->saveSession($request);
  108. return $response;
  109. }
  110. /**
  111. * Start the session for the given request.
  112. *
  113. * @param \Illuminate\Http\Request $request
  114. * @param \Illuminate\Contracts\Session\Session $session
  115. * @return \Illuminate\Contracts\Session\Session
  116. */
  117. protected function startSession(Request $request, $session)
  118. {
  119. return tap($session, function ($session) use ($request) {
  120. $session->setRequestOnHandler($request);
  121. $session->start();
  122. });
  123. }
  124. /**
  125. * Get the session implementation from the manager.
  126. *
  127. * @param \Illuminate\Http\Request $request
  128. * @return \Illuminate\Contracts\Session\Session
  129. */
  130. public function getSession(Request $request)
  131. {
  132. return tap($this->manager->driver(), function ($session) use ($request) {
  133. $session->setId($request->cookies->get($session->getName()));
  134. });
  135. }
  136. /**
  137. * Remove the garbage from the session if necessary.
  138. *
  139. * @param \Illuminate\Contracts\Session\Session $session
  140. * @return void
  141. */
  142. protected function collectGarbage(Session $session)
  143. {
  144. $config = $this->manager->getSessionConfig();
  145. // Here we will see if this request hits the garbage collection lottery by hitting
  146. // the odds needed to perform garbage collection on any given request. If we do
  147. // hit it, we'll call this handler to let it delete all the expired sessions.
  148. if ($this->configHitsLottery($config)) {
  149. $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
  150. }
  151. }
  152. /**
  153. * Determine if the configuration odds hit the lottery.
  154. *
  155. * @param array $config
  156. * @return bool
  157. */
  158. protected function configHitsLottery(array $config)
  159. {
  160. return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
  161. }
  162. /**
  163. * Store the current URL for the request if necessary.
  164. *
  165. * @param \Illuminate\Http\Request $request
  166. * @param \Illuminate\Contracts\Session\Session $session
  167. * @return void
  168. */
  169. protected function storeCurrentUrl(Request $request, $session)
  170. {
  171. if ($request->method() === 'GET' &&
  172. $request->route() &&
  173. ! $request->ajax() &&
  174. ! $request->prefetch()) {
  175. $session->setPreviousUrl($request->fullUrl());
  176. }
  177. }
  178. /**
  179. * Add the session cookie to the application response.
  180. *
  181. * @param \Symfony\Component\HttpFoundation\Response $response
  182. * @param \Illuminate\Contracts\Session\Session $session
  183. * @return void
  184. */
  185. protected function addCookieToResponse(Response $response, Session $session)
  186. {
  187. if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
  188. $response->headers->setCookie(new Cookie(
  189. $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
  190. $config['path'], $config['domain'], $config['secure'] ?? false,
  191. $config['http_only'] ?? true, false, $config['same_site'] ?? null
  192. ));
  193. }
  194. }
  195. /**
  196. * Save the session data to storage.
  197. *
  198. * @param \Illuminate\Http\Request $request
  199. * @return void
  200. */
  201. protected function saveSession($request)
  202. {
  203. $this->manager->driver()->save();
  204. }
  205. /**
  206. * Get the session lifetime in seconds.
  207. *
  208. * @return int
  209. */
  210. protected function getSessionLifetimeInSeconds()
  211. {
  212. return ($this->manager->getSessionConfig()['lifetime'] ?? null) * 60;
  213. }
  214. /**
  215. * Get the cookie lifetime in seconds.
  216. *
  217. * @return \DateTimeInterface|int
  218. */
  219. protected function getCookieExpirationDate()
  220. {
  221. $config = $this->manager->getSessionConfig();
  222. return $config['expire_on_close'] ? 0 : Date::instance(
  223. Carbon::now()->addRealMinutes($config['lifetime'])
  224. );
  225. }
  226. /**
  227. * Determine if a session driver has been configured.
  228. *
  229. * @return bool
  230. */
  231. protected function sessionConfigured()
  232. {
  233. return ! is_null($this->manager->getSessionConfig()['driver'] ?? null);
  234. }
  235. /**
  236. * Determine if the configured session driver is persistent.
  237. *
  238. * @param array|null $config
  239. * @return bool
  240. */
  241. protected function sessionIsPersistent(array $config = null)
  242. {
  243. $config = $config ?: $this->manager->getSessionConfig();
  244. return ! is_null($config['driver'] ?? null);
  245. }
  246. /**
  247. * Resolve the given cache driver.
  248. *
  249. * @param string $cache
  250. * @return \Illuminate\Cache\Store
  251. */
  252. protected function cache($driver)
  253. {
  254. return call_user_func($this->cacheFactoryResolver)->driver($driver);
  255. }
  256. }