RedisSessionHandler.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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. use Predis\Response\ErrorInterface;
  12. use Relay\Relay;
  13. /**
  14. * Redis based session storage handler based on the Redis class
  15. * provided by the PHP redis extension.
  16. *
  17. * @author Dalibor Karlović <dalibor@flexolabs.io>
  18. */
  19. class RedisSessionHandler extends AbstractSessionHandler
  20. {
  21. /**
  22. * Key prefix for shared environments.
  23. */
  24. private string $prefix;
  25. /**
  26. * Time to live in seconds.
  27. */
  28. private int|\Closure|null $ttl;
  29. /**
  30. * List of available options:
  31. * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server
  32. * * ttl: The time to live in seconds.
  33. *
  34. * @throws \InvalidArgumentException When unsupported client or options are passed
  35. */
  36. public function __construct(
  37. private \Redis|Relay|\RedisArray|\RedisCluster|\Predis\ClientInterface $redis,
  38. array $options = [],
  39. ) {
  40. if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) {
  41. throw new \InvalidArgumentException(sprintf('The following options are not supported "%s".', implode(', ', $diff)));
  42. }
  43. $this->prefix = $options['prefix'] ?? 'sf_s';
  44. $this->ttl = $options['ttl'] ?? null;
  45. }
  46. protected function doRead(#[\SensitiveParameter] string $sessionId): string
  47. {
  48. return $this->redis->get($this->prefix.$sessionId) ?: '';
  49. }
  50. protected function doWrite(#[\SensitiveParameter] string $sessionId, string $data): bool
  51. {
  52. $ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime');
  53. $result = $this->redis->setEx($this->prefix.$sessionId, (int) $ttl, $data);
  54. return $result && !$result instanceof ErrorInterface;
  55. }
  56. protected function doDestroy(#[\SensitiveParameter] string $sessionId): bool
  57. {
  58. static $unlink = true;
  59. if ($unlink) {
  60. try {
  61. $unlink = false !== $this->redis->unlink($this->prefix.$sessionId);
  62. } catch (\Throwable) {
  63. $unlink = false;
  64. }
  65. }
  66. if (!$unlink) {
  67. $this->redis->del($this->prefix.$sessionId);
  68. }
  69. return true;
  70. }
  71. #[\ReturnTypeWillChange]
  72. public function close(): bool
  73. {
  74. return true;
  75. }
  76. public function gc(int $maxlifetime): int|false
  77. {
  78. return 0;
  79. }
  80. public function updateTimestamp(#[\SensitiveParameter] string $sessionId, string $data): bool
  81. {
  82. $ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime');
  83. return $this->redis->expire($this->prefix.$sessionId, (int) $ttl);
  84. }
  85. }