Skip to content

Commit a65f931

Browse files
committed
feat(session): session cache and persistence
1 parent 1376c02 commit a65f931

File tree

6 files changed

+236
-187
lines changed

6 files changed

+236
-187
lines changed

packages/http/src/Session/Managers/DatabaseSessionManager.php

Lines changed: 38 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Tempest\Http\Session\SessionDestroyed;
1212
use Tempest\Http\Session\SessionId;
1313
use Tempest\Http\Session\SessionManager;
14+
use Tempest\Http\Session\SessionCache;
1415
use Tempest\Support\Arr;
1516
use Tempest\Support\Arr\ArrayInterface;
1617

@@ -23,128 +24,88 @@ public function __construct(
2324
private Clock $clock,
2425
private SessionConfig $config,
2526
private Database $database,
27+
private SessionCache $cache,
2628
) {}
2729

2830
public function create(SessionId $id): Session
2931
{
30-
return $this->persist($id);
31-
}
32+
$session = $this->resolve(id:$id);
3233

33-
public function set(SessionId $id, string $key, mixed $value): void
34-
{
35-
$this->persist($id, [...$this->getData($id), ...[$key => $value]]);
36-
}
37-
38-
public function get(SessionId $id, string $key, mixed $default = null): mixed
39-
{
40-
$value = Arr\get_by_key($this->getData($id), $key, $default);
41-
42-
if ($value instanceof ArrayInterface) {
43-
return $value->toArray();
34+
if ($session) {
35+
return $session;
4436
}
4537

46-
return $value;
47-
}
48-
49-
public function all(SessionId $id): array
50-
{
51-
return $this->getData($id);
52-
}
38+
$session = new Session(
39+
id: $id,
40+
createdAt: $this->clock->now(),
41+
lastActiveAt: $this->clock->now(),
42+
data: [],
43+
);
5344

54-
public function remove(SessionId $id, string $key): void
55-
{
56-
$data = $this->getData($id);
57-
$data = Arr\remove_keys($data, $key);
45+
$this->cache->store(session:$session);
5846

59-
$this->persist($id, $data);
47+
return $session;
6048
}
6149

6250
public function destroy(SessionId $id): void
6351
{
64-
query(DatabaseSession::class)
52+
query(model:DatabaseSession::class)
6553
->delete()
6654
->where('session_id', (string) $id)
6755
->execute();
6856

69-
event(new SessionDestroyed($id));
70-
}
71-
72-
public function isValid(SessionId $id): bool
73-
{
74-
$session = $this->resolve($id);
75-
76-
if ($session === null) {
77-
return false;
78-
}
79-
80-
return $this->clock->now()->before(
81-
other: $session->lastActiveAt->plus($this->config->expiration),
82-
);
57+
event(event:new SessionDestroyed(id:$id));
8358
}
8459

8560
public function cleanup(): void
8661
{
8762
$expired = $this->clock
8863
->now()
89-
->minus($this->config->expiration);
64+
->minus(duration:$this->config->expiration);
9065

91-
query(DatabaseSession::class)
66+
query(model:DatabaseSession::class)
9267
->delete()
93-
->whereBefore('last_active_at', $expired)
68+
->whereBefore(field:'last_active_at', date:$expired)
9469
->execute();
9570
}
9671

97-
private function resolve(SessionId $id): ?Session
72+
public function resolve(SessionId $id): ?Session
9873
{
99-
$session = query(DatabaseSession::class)
74+
$session = $this->cache->find(sessionId:$id);
75+
76+
if ($session) {
77+
return $session;
78+
}
79+
80+
$session = query(model:DatabaseSession::class)
10081
->select()
10182
->where('session_id', (string) $id)
10283
->first();
10384

104-
if ($session === null) {
85+
if (!$session) {
10586
return null;
10687
}
10788

108-
return new Session(
89+
$session = new Session(
10990
id: $id,
11091
createdAt: $session->created_at,
11192
lastActiveAt: $session->last_active_at,
112-
data: unserialize($session->data),
93+
data: unserialize(data:$session->data),
11394
);
114-
}
11595

116-
/**
117-
* @return array<mixed>
118-
*/
119-
private function getData(SessionId $id): array
120-
{
121-
return $this->resolve($id)->data ?? [];
96+
$this->cache->store(session:$session);
97+
98+
return $session;
12299
}
123100

124-
/**
125-
* @param array<mixed>|null $data
126-
*/
127-
private function persist(SessionId $id, ?array $data = null): Session
101+
public function persist(Session $session): void
128102
{
129-
$now = $this->clock->now();
130-
$session = $this->resolve($id) ?? new Session(
131-
id: $id,
132-
createdAt: $now,
133-
lastActiveAt: $now,
134-
);
135-
136-
if ($data !== null) {
137-
$session->data = $data;
138-
}
139-
140-
query(DatabaseSession::class)->updateOrCreate([
141-
'session_id' => (string) $id,
142-
], [
143-
'data' => serialize($session->data),
103+
query(model:DatabaseSession::class)->updateOrCreate(find:[
104+
'session_id' => (string) $session->id,
105+
], update:[
106+
'data' => serialize(value:$session->data),
144107
'created_at' => $session->createdAt,
145-
'last_active_at' => $now,
108+
'last_active_at' => $this->clock->now(),
146109
]);
147-
148-
return $session;
149110
}
150111
}

packages/http/src/Session/Managers/FileSessionManager.php

Lines changed: 44 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Tempest\Clock\Clock;
88
use Tempest\Http\Session\Session;
9+
use Tempest\Http\Session\SessionCache;
910
use Tempest\Http\Session\SessionConfig;
1011
use Tempest\Http\Session\SessionDestroyed;
1112
use Tempest\Http\Session\SessionId;
@@ -21,138 +22,99 @@
2122
public function __construct(
2223
private Clock $clock,
2324
private SessionConfig $sessionConfig,
25+
private SessionCache $cache,
2426
) {}
2527

2628
public function create(SessionId $id): Session
2729
{
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-
}
30+
$session = $this->resolve(id:$id);
3531

36-
public function get(SessionId $id, string $key, mixed $default = null): mixed
37-
{
38-
return $this->getData($id)[$key] ?? $default;
39-
}
32+
if ($session) {
33+
return $session;
34+
}
4035

41-
public function remove(SessionId $id, string $key): void
42-
{
43-
$data = $this->getData($id);
36+
$session = new Session(
37+
id: $id,
38+
createdAt: $this->clock->now(),
39+
lastActiveAt: $this->clock->now(),
40+
data: [],
41+
);
4442

45-
unset($data[$key]);
43+
$this->cache->store(session:$session);
4644

47-
$this->persist($id, $data);
45+
return $session;
4846
}
4947

5048
public function destroy(SessionId $id): void
5149
{
52-
unlink($this->getPath($id));
50+
unlink(filename:$this->getPath(id:$id));
5351

54-
event(new SessionDestroyed($id));
52+
event(event:new SessionDestroyed(id:$id));
5553
}
5654

57-
public function isValid(SessionId $id): bool
55+
public function resolve(SessionId $id): ?Session
5856
{
59-
$session = $this->resolve($id);
57+
$session = $this->cache->find(sessionId:$id);
6058

61-
if ($session === null) {
62-
return false;
59+
if ($session) {
60+
return $session;
6361
}
6462

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 getPath(SessionId $id): string
75-
{
76-
return internal_storage_path($this->sessionConfig->path, (string) $id);
77-
}
78-
79-
private function resolve(SessionId $id): ?Session
80-
{
81-
$path = $this->getPath($id);
63+
$path = $this->getPath(id:$id);
8264

8365
try {
84-
if (! Filesystem\is_file($path)) {
66+
if (! Filesystem\is_file(path:$path)) {
8567
return null;
8668
}
8769

88-
$file_pointer = fopen($path, 'rb');
89-
flock($file_pointer, LOCK_SH);
70+
$file_pointer = fopen(filename:$path, mode:'rb');
71+
flock(stream:$file_pointer, operation:LOCK_SH);
72+
73+
$content = Filesystem\read_file(filename:$path);
74+
75+
flock(stream:$file_pointer, operation:LOCK_UN);
76+
fclose(stream:$file_pointer);
9077

91-
$content = Filesystem\read_file($path);
78+
$session = unserialize(data:$content, options:['allowed_classes' => true]);
9279

93-
flock($file_pointer, LOCK_UN);
94-
fclose($file_pointer);
80+
$this->cache->store(session:$session);
9581

96-
return unserialize($content, ['allowed_classes' => true]);
82+
return $session;
9783
} catch (Throwable) {
9884
return null;
9985
}
10086
}
10187

102-
public function all(SessionId $id): array
88+
public function persist(Session $session): void
10389
{
104-
return $this->getData($id);
105-
}
90+
$session->lastActiveAt = $this->clock->now();
10691

107-
/**
108-
* @return array<mixed>
109-
*/
110-
private function getData(SessionId $id): array
111-
{
112-
return $this->resolve($id)->data ?? [];
113-
}
114-
115-
/**
116-
* @param array<mixed>|null $data
117-
*/
118-
private function persist(SessionId $id, ?array $data = null): Session
119-
{
120-
$now = $this->clock->now();
121-
$session = $this->resolve($id) ?? new Session(
122-
id: $id,
123-
createdAt: $now,
124-
lastActiveAt: $now,
125-
);
126-
127-
$session->lastActiveAt = $now;
128-
129-
if ($data !== null) {
130-
$session->data = $data;
131-
}
132-
133-
Filesystem\write_file($this->getPath($id), serialize($session), LOCK_EX);
134-
135-
return $session;
92+
Filesystem\write_file(filename:$this->getPath(id:$session->id), content:serialize(value:$session), flags:LOCK_EX);
13693
}
13794

13895
public function cleanup(): void
13996
{
140-
$sessionFiles = glob(internal_storage_path($this->sessionConfig->path, '/*'));
97+
$sessionFiles = glob(pattern:internal_storage_path($this->sessionConfig->path, '/*'));
14198

14299
foreach ($sessionFiles as $sessionFile) {
143-
$id = new SessionId(pathinfo($sessionFile, PATHINFO_FILENAME));
100+
$id = new SessionId(id:pathinfo(path:$sessionFile, flags:PATHINFO_FILENAME));
144101

145-
$session = $this->resolve($id);
102+
$session = $this->resolve(id:$id);
146103

147104
if ($session === null) {
148105
continue;
149106
}
150107

151-
if ($this->isValid($session->id)) {
108+
if ($this->cache->isValid(session:$session)) {
152109
continue;
153110
}
154111

155112
$session->destroy();
156113
}
157114
}
115+
116+
private function getPath(SessionId $id): string
117+
{
118+
return internal_storage_path($this->sessionConfig->path, (string) $id);
119+
}
158120
}

0 commit comments

Comments
 (0)