Store.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <?php
  2. namespace Illuminate\Session;
  3. use Closure;
  4. use stdClass;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Str;
  7. use SessionHandlerInterface;
  8. use Illuminate\Contracts\Session\Session;
  9. class Store implements Session
  10. {
  11. /**
  12. * The session ID.
  13. *
  14. * @var string
  15. */
  16. protected $id;
  17. /**
  18. * The session name.
  19. *
  20. * @var string
  21. */
  22. protected $name;
  23. /**
  24. * The session attributes.
  25. *
  26. * @var array
  27. */
  28. protected $attributes = [];
  29. /**
  30. * The session handler implementation.
  31. *
  32. * @var \SessionHandlerInterface
  33. */
  34. protected $handler;
  35. /**
  36. * Session store started status.
  37. *
  38. * @var bool
  39. */
  40. protected $started = false;
  41. /**
  42. * Create a new session instance.
  43. *
  44. * @param string $name
  45. * @param \SessionHandlerInterface $handler
  46. * @param string|null $id
  47. * @return void
  48. */
  49. public function __construct($name, SessionHandlerInterface $handler, $id = null)
  50. {
  51. $this->setId($id);
  52. $this->name = $name;
  53. $this->handler = $handler;
  54. }
  55. /**
  56. * Start the session, reading the data from a handler.
  57. *
  58. * @return bool
  59. */
  60. public function start()
  61. {
  62. $this->loadSession();
  63. if (! $this->has('_token')) {
  64. $this->regenerateToken();
  65. }
  66. return $this->started = true;
  67. }
  68. /**
  69. * Load the session data from the handler.
  70. *
  71. * @return void
  72. */
  73. protected function loadSession()
  74. {
  75. $this->attributes = array_merge($this->attributes, $this->readFromHandler());
  76. }
  77. /**
  78. * Read the session data from the handler.
  79. *
  80. * @return array
  81. */
  82. protected function readFromHandler()
  83. {
  84. if ($data = $this->handler->read($this->getId())) {
  85. $data = @unserialize($this->prepareForUnserialize($data));
  86. if ($data !== false && ! is_null($data) && is_array($data)) {
  87. return $data;
  88. }
  89. }
  90. return [];
  91. }
  92. /**
  93. * Prepare the raw string data from the session for unserialization.
  94. *
  95. * @param string $data
  96. * @return string
  97. */
  98. protected function prepareForUnserialize($data)
  99. {
  100. return $data;
  101. }
  102. /**
  103. * Save the session data to storage.
  104. *
  105. * @return bool
  106. */
  107. public function save()
  108. {
  109. $this->ageFlashData();
  110. $this->handler->write($this->getId(), $this->prepareForStorage(
  111. serialize($this->attributes)
  112. ));
  113. $this->started = false;
  114. }
  115. /**
  116. * Prepare the serialized session data for storage.
  117. *
  118. * @param string $data
  119. * @return string
  120. */
  121. protected function prepareForStorage($data)
  122. {
  123. return $data;
  124. }
  125. /**
  126. * Age the flash data for the session.
  127. *
  128. * @return void
  129. */
  130. public function ageFlashData()
  131. {
  132. $this->forget($this->get('_flash.old', []));
  133. $this->put('_flash.old', $this->get('_flash.new', []));
  134. $this->put('_flash.new', []);
  135. }
  136. /**
  137. * Get all of the session data.
  138. *
  139. * @return array
  140. */
  141. public function all()
  142. {
  143. return $this->attributes;
  144. }
  145. /**
  146. * Checks if a key exists.
  147. *
  148. * @param string|array $key
  149. * @return bool
  150. */
  151. public function exists($key)
  152. {
  153. $placeholder = new stdClass();
  154. return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) use ($placeholder) {
  155. return $this->get($key, $placeholder) === $placeholder;
  156. });
  157. }
  158. /**
  159. * Checks if a key is present and not null.
  160. *
  161. * @param string|array $key
  162. * @return bool
  163. */
  164. public function has($key)
  165. {
  166. return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) {
  167. return is_null($this->get($key));
  168. });
  169. }
  170. /**
  171. * Get an item from the session.
  172. *
  173. * @param string $key
  174. * @param mixed $default
  175. * @return mixed
  176. */
  177. public function get($key, $default = null)
  178. {
  179. return Arr::get($this->attributes, $key, $default);
  180. }
  181. /**
  182. * Get the value of a given key and then forget it.
  183. *
  184. * @param string $key
  185. * @param string $default
  186. * @return mixed
  187. */
  188. public function pull($key, $default = null)
  189. {
  190. return Arr::pull($this->attributes, $key, $default);
  191. }
  192. /**
  193. * Determine if the session contains old input.
  194. *
  195. * @param string $key
  196. * @return bool
  197. */
  198. public function hasOldInput($key = null)
  199. {
  200. $old = $this->getOldInput($key);
  201. return is_null($key) ? count($old) > 0 : ! is_null($old);
  202. }
  203. /**
  204. * Get the requested item from the flashed input array.
  205. *
  206. * @param string $key
  207. * @param mixed $default
  208. * @return mixed
  209. */
  210. public function getOldInput($key = null, $default = null)
  211. {
  212. return Arr::get($this->get('_old_input', []), $key, $default);
  213. }
  214. /**
  215. * Replace the given session attributes entirely.
  216. *
  217. * @param array $attributes
  218. * @return void
  219. */
  220. public function replace(array $attributes)
  221. {
  222. $this->put($attributes);
  223. }
  224. /**
  225. * Put a key / value pair or array of key / value pairs in the session.
  226. *
  227. * @param string|array $key
  228. * @param mixed $value
  229. * @return void
  230. */
  231. public function put($key, $value = null)
  232. {
  233. if (! is_array($key)) {
  234. $key = [$key => $value];
  235. }
  236. foreach ($key as $arrayKey => $arrayValue) {
  237. Arr::set($this->attributes, $arrayKey, $arrayValue);
  238. }
  239. }
  240. /**
  241. * Get an item from the session, or store the default value.
  242. *
  243. * @param string $key
  244. * @param \Closure $callback
  245. * @return mixed
  246. */
  247. public function remember($key, Closure $callback)
  248. {
  249. if (! is_null($value = $this->get($key))) {
  250. return $value;
  251. }
  252. return tap($callback(), function ($value) use ($key) {
  253. $this->put($key, $value);
  254. });
  255. }
  256. /**
  257. * Push a value onto a session array.
  258. *
  259. * @param string $key
  260. * @param mixed $value
  261. * @return void
  262. */
  263. public function push($key, $value)
  264. {
  265. $array = $this->get($key, []);
  266. $array[] = $value;
  267. $this->put($key, $array);
  268. }
  269. /**
  270. * Increment the value of an item in the session.
  271. *
  272. * @param string $key
  273. * @param int $amount
  274. * @return mixed
  275. */
  276. public function increment($key, $amount = 1)
  277. {
  278. $this->put($key, $value = $this->get($key, 0) + $amount);
  279. return $value;
  280. }
  281. /**
  282. * Decrement the value of an item in the session.
  283. *
  284. * @param string $key
  285. * @param int $amount
  286. * @return int
  287. */
  288. public function decrement($key, $amount = 1)
  289. {
  290. return $this->increment($key, $amount * -1);
  291. }
  292. /**
  293. * Flash a key / value pair to the session.
  294. *
  295. * @param string $key
  296. * @param mixed $value
  297. * @return void
  298. */
  299. public function flash(string $key, $value = true)
  300. {
  301. $this->put($key, $value);
  302. $this->push('_flash.new', $key);
  303. $this->removeFromOldFlashData([$key]);
  304. }
  305. /**
  306. * Flash a key / value pair to the session for immediate use.
  307. *
  308. * @param string $key
  309. * @param mixed $value
  310. * @return void
  311. */
  312. public function now($key, $value)
  313. {
  314. $this->put($key, $value);
  315. $this->push('_flash.old', $key);
  316. }
  317. /**
  318. * Reflash all of the session flash data.
  319. *
  320. * @return void
  321. */
  322. public function reflash()
  323. {
  324. $this->mergeNewFlashes($this->get('_flash.old', []));
  325. $this->put('_flash.old', []);
  326. }
  327. /**
  328. * Reflash a subset of the current flash data.
  329. *
  330. * @param array|mixed $keys
  331. * @return void
  332. */
  333. public function keep($keys = null)
  334. {
  335. $this->mergeNewFlashes($keys = is_array($keys) ? $keys : func_get_args());
  336. $this->removeFromOldFlashData($keys);
  337. }
  338. /**
  339. * Merge new flash keys into the new flash array.
  340. *
  341. * @param array $keys
  342. * @return void
  343. */
  344. protected function mergeNewFlashes(array $keys)
  345. {
  346. $values = array_unique(array_merge($this->get('_flash.new', []), $keys));
  347. $this->put('_flash.new', $values);
  348. }
  349. /**
  350. * Remove the given keys from the old flash data.
  351. *
  352. * @param array $keys
  353. * @return void
  354. */
  355. protected function removeFromOldFlashData(array $keys)
  356. {
  357. $this->put('_flash.old', array_diff($this->get('_flash.old', []), $keys));
  358. }
  359. /**
  360. * Flash an input array to the session.
  361. *
  362. * @param array $value
  363. * @return void
  364. */
  365. public function flashInput(array $value)
  366. {
  367. $this->flash('_old_input', $value);
  368. }
  369. /**
  370. * Remove an item from the session, returning its value.
  371. *
  372. * @param string $key
  373. * @return mixed
  374. */
  375. public function remove($key)
  376. {
  377. return Arr::pull($this->attributes, $key);
  378. }
  379. /**
  380. * Remove one or many items from the session.
  381. *
  382. * @param string|array $keys
  383. * @return void
  384. */
  385. public function forget($keys)
  386. {
  387. Arr::forget($this->attributes, $keys);
  388. }
  389. /**
  390. * Remove all of the items from the session.
  391. *
  392. * @return void
  393. */
  394. public function flush()
  395. {
  396. $this->attributes = [];
  397. }
  398. /**
  399. * Flush the session data and regenerate the ID.
  400. *
  401. * @return bool
  402. */
  403. public function invalidate()
  404. {
  405. $this->flush();
  406. return $this->migrate(true);
  407. }
  408. /**
  409. * Generate a new session identifier.
  410. *
  411. * @param bool $destroy
  412. * @return bool
  413. */
  414. public function regenerate($destroy = false)
  415. {
  416. return tap($this->migrate($destroy), function () {
  417. $this->regenerateToken();
  418. });
  419. }
  420. /**
  421. * Generate a new session ID for the session.
  422. *
  423. * @param bool $destroy
  424. * @return bool
  425. */
  426. public function migrate($destroy = false)
  427. {
  428. if ($destroy) {
  429. $this->handler->destroy($this->getId());
  430. }
  431. $this->setExists(false);
  432. $this->setId($this->generateSessionId());
  433. return true;
  434. }
  435. /**
  436. * Determine if the session has been started.
  437. *
  438. * @return bool
  439. */
  440. public function isStarted()
  441. {
  442. return $this->started;
  443. }
  444. /**
  445. * Get the name of the session.
  446. *
  447. * @return string
  448. */
  449. public function getName()
  450. {
  451. return $this->name;
  452. }
  453. /**
  454. * Set the name of the session.
  455. *
  456. * @param string $name
  457. * @return void
  458. */
  459. public function setName($name)
  460. {
  461. $this->name = $name;
  462. }
  463. /**
  464. * Get the current session ID.
  465. *
  466. * @return string
  467. */
  468. public function getId()
  469. {
  470. return $this->id;
  471. }
  472. /**
  473. * Set the session ID.
  474. *
  475. * @param string $id
  476. * @return void
  477. */
  478. public function setId($id)
  479. {
  480. $this->id = $this->isValidId($id) ? $id : $this->generateSessionId();
  481. }
  482. /**
  483. * Determine if this is a valid session ID.
  484. *
  485. * @param string $id
  486. * @return bool
  487. */
  488. public function isValidId($id)
  489. {
  490. return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
  491. }
  492. /**
  493. * Get a new, random session ID.
  494. *
  495. * @return string
  496. */
  497. protected function generateSessionId()
  498. {
  499. return Str::random(40);
  500. }
  501. /**
  502. * Set the existence of the session on the handler if applicable.
  503. *
  504. * @param bool $value
  505. * @return void
  506. */
  507. public function setExists($value)
  508. {
  509. if ($this->handler instanceof ExistenceAwareInterface) {
  510. $this->handler->setExists($value);
  511. }
  512. }
  513. /**
  514. * Get the CSRF token value.
  515. *
  516. * @return string
  517. */
  518. public function token()
  519. {
  520. return $this->get('_token');
  521. }
  522. /**
  523. * Regenerate the CSRF token value.
  524. *
  525. * @return void
  526. */
  527. public function regenerateToken()
  528. {
  529. $this->put('_token', Str::random(40));
  530. }
  531. /**
  532. * Get the previous URL from the session.
  533. *
  534. * @return string|null
  535. */
  536. public function previousUrl()
  537. {
  538. return $this->get('_previous.url');
  539. }
  540. /**
  541. * Set the "previous" URL in the session.
  542. *
  543. * @param string $url
  544. * @return void
  545. */
  546. public function setPreviousUrl($url)
  547. {
  548. $this->put('_previous.url', $url);
  549. }
  550. /**
  551. * Get the underlying session handler implementation.
  552. *
  553. * @return \SessionHandlerInterface
  554. */
  555. public function getHandler()
  556. {
  557. return $this->handler;
  558. }
  559. /**
  560. * Determine if the session handler needs a request.
  561. *
  562. * @return bool
  563. */
  564. public function handlerNeedsRequest()
  565. {
  566. return $this->handler instanceof CookieSessionHandler;
  567. }
  568. /**
  569. * Set the request on the handler instance.
  570. *
  571. * @param \Illuminate\Http\Request $request
  572. * @return void
  573. */
  574. public function setRequestOnHandler($request)
  575. {
  576. if ($this->handlerNeedsRequest()) {
  577. $this->handler->setRequest($request);
  578. }
  579. }
  580. }