Skip to content

Commit 08ddd5b

Browse files
committed
WIP
1 parent f9c246d commit 08ddd5b

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Http\Session\Config;
6+
7+
use Tempest\Container\Container;
8+
use Tempest\DateTime\Duration;
9+
use Tempest\Http\Session\Managers\RedisSessionManager;
10+
use Tempest\Http\Session\SessionConfig;
11+
12+
final class RedisSessionConfig implements SessionConfig
13+
{
14+
/**
15+
* @param Duration $expiration Time required for a session to expire.
16+
*/
17+
public function __construct(
18+
private(set) Duration $expiration,
19+
private(set) string $prefix = 'session',
20+
) {}
21+
22+
public function createManager(Container $container): RedisSessionManager
23+
{
24+
return $container->get(RedisSessionManager::class);
25+
}
26+
27+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Http\Session\Managers;
6+
7+
use Tempest\Clock\Clock;
8+
use Tempest\Http\Session\Session;
9+
use Tempest\Http\Session\SessionConfig;
10+
use Tempest\Http\Session\SessionDestroyed;
11+
use Tempest\Http\Session\SessionId;
12+
use Tempest\Http\Session\SessionManager;
13+
use Tempest\KeyValue\Redis\Redis;
14+
use Tempest\Support\Filesystem;
15+
use Throwable;
16+
use function Tempest\event;
17+
18+
final readonly class RedisSessionManager implements SessionManager
19+
{
20+
public function __construct(
21+
private Clock $clock,
22+
private Redis $redis,
23+
private SessionConfig $sessionConfig,
24+
) {}
25+
26+
public function create(SessionId $id): Session
27+
{
28+
return $this->persist($id);
29+
}
30+
31+
public function set(SessionId $id, string $key, mixed $value): void
32+
{
33+
$this->persist($id, [...$this->getData($id), ...[$key => $value]]);
34+
}
35+
36+
public function get(SessionId $id, string $key, mixed $default = null): mixed
37+
{
38+
return $this->getData($id)[$key] ?? $default;
39+
}
40+
41+
public function remove(SessionId $id, string $key): void
42+
{
43+
$data = $this->getData($id);
44+
45+
unset($data[$key]);
46+
47+
$this->persist($id, $data);
48+
}
49+
50+
public function destroy(SessionId $id): void
51+
{
52+
$this->redis->getClient()->del($this->getKey($id));
53+
54+
event(new SessionDestroyed($id));
55+
}
56+
57+
public function isValid(SessionId $id): bool
58+
{
59+
$session = $this->resolve($id);
60+
61+
if ($session === null) {
62+
return false;
63+
}
64+
65+
if (! ($session->lastActiveAt ?? null)) {
66+
return false;
67+
}
68+
69+
return $this->clock->now()->before(
70+
other: $session->lastActiveAt->plus($this->sessionConfig->expiration),
71+
);
72+
}
73+
74+
private function resolve(SessionId $id): ?Session
75+
{
76+
try {
77+
$content = $this->redis->get($this->getKey($id));
78+
return unserialize($content, ['allowed_classes' => true]);
79+
} catch (Throwable $e) {
80+
return null;
81+
}
82+
}
83+
84+
public function all(SessionId $id): array
85+
{
86+
return $this->getData($id);
87+
}
88+
89+
/**
90+
* @return array<mixed>
91+
*/
92+
private function getData(SessionId $id): array
93+
{
94+
return $this->resolve($id)->data ?? [];
95+
}
96+
97+
/**
98+
* @param array<mixed>|null $data
99+
*/
100+
private function persist(SessionId $id, ?array $data = null): Session
101+
{
102+
$now = $this->clock->now();
103+
$session = $this->resolve($id) ?? new Session(
104+
id: $id,
105+
createdAt: $now,
106+
lastActiveAt: $now,
107+
);
108+
109+
$session->lastActiveAt = $now;
110+
111+
if ($data !== null) {
112+
$session->data = $data;
113+
}
114+
115+
$this->redis->set($this->getKey($id), serialize($session), $this->sessionConfig->expiration);
116+
117+
return $session;
118+
}
119+
120+
private function getKey(SessionId $id): string
121+
{
122+
return sprintf('%s_%s', $this->sessionConfig->prefix, $id);
123+
}
124+
125+
public function cleanup(): void
126+
{
127+
// what should we do here?
128+
// on persist we set the expiration (ttl) for the session in the redis store
129+
// in theory all session data should be expire by itself
130+
}
131+
}

0 commit comments

Comments
 (0)