Skip to content

Commit c1896e9

Browse files
laylatichygitbutler-client
authored andcommitted
feat(session): session cache and persistence
1 parent 1376c02 commit c1896e9

File tree

6 files changed

+205
-156
lines changed

6 files changed

+205
-156
lines changed

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

Lines changed: 28 additions & 67 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,40 +24,27 @@ 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-
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);
32+
$session = $this->resolve($id);
4133

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);
5846

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

6250
public function destroy(SessionId $id): void
@@ -69,19 +57,6 @@ public function destroy(SessionId $id): void
6957
event(new SessionDestroyed($id));
7058
}
7159

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-
);
83-
}
84-
8560
public function cleanup(): void
8661
{
8762
$expired = $this->clock
@@ -94,57 +69,43 @@ public function cleanup(): void
9469
->execute();
9570
}
9671

97-
private function resolve(SessionId $id): ?Session
72+
public function resolve(SessionId $id): ?Session
9873
{
74+
$session = $this->cache->find($id);
75+
76+
if ($session) {
77+
return $session;
78+
}
79+
9980
$session = query(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,
11293
data: unserialize($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);
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-
140103
query(DatabaseSession::class)->updateOrCreate([
141-
'session_id' => (string) $id,
104+
'session_id' => (string) $session->id,
142105
], [
143106
'data' => serialize($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: 32 additions & 70 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,30 +22,27 @@
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);
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);
4644

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

5048
public function destroy(SessionId $id): void
@@ -54,30 +52,14 @@ public function destroy(SessionId $id): void
5452
event(new SessionDestroyed($id));
5553
}
5654

57-
public function isValid(SessionId $id): bool
55+
public function resolve(SessionId $id): ?Session
5856
{
59-
$session = $this->resolve($id);
60-
61-
if ($session === null) {
62-
return false;
63-
}
57+
$session = $this->cache->find($id);
6458

65-
if (! ($session->lastActiveAt ?? null)) {
66-
return false;
59+
if ($session) {
60+
return $session;
6761
}
6862

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-
{
8163
$path = $this->getPath($id);
8264

8365
try {
@@ -93,46 +75,21 @@ private function resolve(SessionId $id): ?Session
9375
flock($file_pointer, LOCK_UN);
9476
fclose($file_pointer);
9577

96-
return unserialize($content, ['allowed_classes' => true]);
78+
$session = unserialize($content, ['allowed_classes' => true]);
79+
80+
$this->cache->store($session);
81+
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-
}
106-
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);
90+
$session->lastActiveAt = $this->clock->now();
13491

135-
return $session;
92+
Filesystem\write_file($this->getPath($session->id), serialize($session), LOCK_EX);
13693
}
13794

13895
public function cleanup(): void
@@ -148,11 +105,16 @@ public function cleanup(): void
148105
continue;
149106
}
150107

151-
if ($this->isValid($session->id)) {
108+
if ($this->cache->isValid($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
}

packages/http/src/Session/Session.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ final class Session
2121

2222
private array $expiredKeys = [];
2323

24-
private SessionManager $manager {
25-
get => get(SessionManager::class);
24+
private SessionCache $cache {
25+
get => get(SessionCache::class);
2626
}
2727

2828
/**
@@ -48,23 +48,23 @@ public function __construct(
4848

4949
public function set(string $key, mixed $value): void
5050
{
51-
$this->manager->set($this->id, $key, $value);
51+
$this->cache->set($this->id, $key, $value);
5252
}
5353

5454
/**
5555
* Stores a value in the session that will be available for the next request only.
5656
*/
5757
public function flash(string $key, mixed $value): void
5858
{
59-
$this->manager->set($this->id, $key, new FlashValue($value));
59+
$this->cache->set($this->id, $key, new FlashValue($value));
6060
}
6161

6262
/**
6363
* Reflashes all flash values in the session, making them available for the next request.
6464
*/
6565
public function reflash(): void
6666
{
67-
foreach ($this->manager->all($this->id) as $key => $value) {
67+
foreach ($this->cache->all($this->id) as $key => $value) {
6868
if (! $value instanceof FlashValue) {
6969
continue;
7070
}
@@ -75,7 +75,7 @@ public function reflash(): void
7575

7676
public function get(string $key, mixed $default = null): mixed
7777
{
78-
$value = $this->manager->get($this->id, $key, $default);
78+
$value = $this->cache->get($this->id, $key, $default);
7979

8080
if ($value instanceof FlashValue) {
8181
$this->expiredKeys[$key] = $key;
@@ -120,28 +120,33 @@ public function consume(string $key, mixed $default = null): mixed
120120

121121
public function all(): array
122122
{
123-
return $this->manager->all($this->id);
123+
return $this->cache->all($this->id);
124124
}
125125

126126
public function remove(string $key): void
127127
{
128-
$this->manager->remove($this->id, $key);
128+
$this->cache->remove($this->id, $key);
129129
}
130130

131131
public function destroy(): void
132132
{
133-
$this->manager->destroy($this->id);
133+
$this->cache->destroy($this->id);
134134
}
135135

136136
public function isValid(): bool
137137
{
138-
return $this->manager->isValid($this->id);
138+
return $this->cache->isValid($this->id);
139+
}
140+
141+
public function persist(): void
142+
{
143+
$this->cache->persist($this->id);
139144
}
140145

141146
public function cleanup(): void
142147
{
143148
foreach ($this->expiredKeys as $key) {
144-
$this->manager->remove($this->id, $key);
149+
$this->cache->remove($this->id, $key);
145150
}
146151
}
147152
}

0 commit comments

Comments
 (0)