Skip to content

Commit 568584e

Browse files
committed
Add BackedEnum and UnitEnum support to Context
Allow using enums as context keys for set, get, has, destroy, override, and getOrSet methods. Follows the same pattern as Session enum support.
1 parent e7b5ee6 commit 568584e

File tree

2 files changed

+274
-0
lines changed

2 files changed

+274
-0
lines changed

src/core/src/Context/Context.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
namespace Hypervel\Context;
66

7+
use BackedEnum;
8+
use Closure;
79
use Hyperf\Context\Context as HyperfContext;
810
use Hyperf\Engine\Coroutine;
11+
use UnitEnum;
12+
13+
use function Hypervel\Support\enum_value;
914

1015
class Context extends HyperfContext
1116
{
@@ -16,6 +21,54 @@ public function __call(string $method, array $arguments): mixed
1621
return static::{$method}(...$arguments);
1722
}
1823

24+
/**
25+
* Set a value in the context.
26+
*/
27+
public static function set(BackedEnum|UnitEnum|string $id, mixed $value, ?int $coroutineId = null): mixed
28+
{
29+
return parent::set(enum_value($id), $value, $coroutineId);
30+
}
31+
32+
/**
33+
* Get a value from the context.
34+
*/
35+
public static function get(BackedEnum|UnitEnum|string $id, mixed $default = null, ?int $coroutineId = null): mixed
36+
{
37+
return parent::get(enum_value($id), $default, $coroutineId);
38+
}
39+
40+
/**
41+
* Determine if a value exists in the context.
42+
*/
43+
public static function has(BackedEnum|UnitEnum|string $id, ?int $coroutineId = null): bool
44+
{
45+
return parent::has(enum_value($id), $coroutineId);
46+
}
47+
48+
/**
49+
* Remove a value from the context.
50+
*/
51+
public static function destroy(BackedEnum|UnitEnum|string $id, ?int $coroutineId = null): void
52+
{
53+
parent::destroy(enum_value($id), $coroutineId);
54+
}
55+
56+
/**
57+
* Retrieve the value and override it by closure.
58+
*/
59+
public static function override(BackedEnum|UnitEnum|string $id, Closure $closure, ?int $coroutineId = null): mixed
60+
{
61+
return parent::override(enum_value($id), $closure, $coroutineId);
62+
}
63+
64+
/**
65+
* Retrieve the value and store it if not exists.
66+
*/
67+
public static function getOrSet(BackedEnum|UnitEnum|string $id, mixed $value, ?int $coroutineId = null): mixed
68+
{
69+
return parent::getOrSet(enum_value($id), $value, $coroutineId);
70+
}
71+
1972
/**
2073
* Set multiple key-value pairs in the context.
2174
*/

tests/Core/ContextEnumTest.php

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Tests\Core;
6+
7+
use Hypervel\Context\Context;
8+
use PHPUnit\Framework\TestCase;
9+
10+
enum ContextKeyBackedEnum: string
11+
{
12+
case CurrentUser = 'current-user';
13+
case RequestId = 'request-id';
14+
case Tenant = 'tenant';
15+
}
16+
17+
enum ContextKeyUnitEnum
18+
{
19+
case Locale;
20+
case Theme;
21+
}
22+
23+
/**
24+
* @internal
25+
* @coversNothing
26+
*/
27+
class ContextEnumTest extends TestCase
28+
{
29+
protected function setUp(): void
30+
{
31+
parent::setUp();
32+
33+
Context::destroyAll();
34+
}
35+
36+
protected function tearDown(): void
37+
{
38+
Context::destroyAll();
39+
parent::tearDown();
40+
}
41+
42+
public function testSetAndGetWithBackedEnum(): void
43+
{
44+
Context::set(ContextKeyBackedEnum::CurrentUser, 'user-123');
45+
46+
$this->assertSame('user-123', Context::get(ContextKeyBackedEnum::CurrentUser));
47+
}
48+
49+
public function testSetAndGetWithUnitEnum(): void
50+
{
51+
Context::set(ContextKeyUnitEnum::Locale, 'en-US');
52+
53+
$this->assertSame('en-US', Context::get(ContextKeyUnitEnum::Locale));
54+
}
55+
56+
public function testHasWithBackedEnum(): void
57+
{
58+
$this->assertFalse(Context::has(ContextKeyBackedEnum::CurrentUser));
59+
60+
Context::set(ContextKeyBackedEnum::CurrentUser, 'user-123');
61+
62+
$this->assertTrue(Context::has(ContextKeyBackedEnum::CurrentUser));
63+
}
64+
65+
public function testHasWithUnitEnum(): void
66+
{
67+
$this->assertFalse(Context::has(ContextKeyUnitEnum::Locale));
68+
69+
Context::set(ContextKeyUnitEnum::Locale, 'en-US');
70+
71+
$this->assertTrue(Context::has(ContextKeyUnitEnum::Locale));
72+
}
73+
74+
public function testDestroyWithBackedEnum(): void
75+
{
76+
Context::set(ContextKeyBackedEnum::CurrentUser, 'user-123');
77+
$this->assertTrue(Context::has(ContextKeyBackedEnum::CurrentUser));
78+
79+
Context::destroy(ContextKeyBackedEnum::CurrentUser);
80+
81+
$this->assertFalse(Context::has(ContextKeyBackedEnum::CurrentUser));
82+
}
83+
84+
public function testDestroyWithUnitEnum(): void
85+
{
86+
Context::set(ContextKeyUnitEnum::Locale, 'en-US');
87+
$this->assertTrue(Context::has(ContextKeyUnitEnum::Locale));
88+
89+
Context::destroy(ContextKeyUnitEnum::Locale);
90+
91+
$this->assertFalse(Context::has(ContextKeyUnitEnum::Locale));
92+
}
93+
94+
public function testOverrideWithBackedEnum(): void
95+
{
96+
Context::set(ContextKeyBackedEnum::CurrentUser, 'user-123');
97+
98+
$result = Context::override(ContextKeyBackedEnum::CurrentUser, fn ($value) => $value . '-modified');
99+
100+
$this->assertSame('user-123-modified', $result);
101+
$this->assertSame('user-123-modified', Context::get(ContextKeyBackedEnum::CurrentUser));
102+
}
103+
104+
public function testOverrideWithUnitEnum(): void
105+
{
106+
Context::set(ContextKeyUnitEnum::Locale, 'en');
107+
108+
$result = Context::override(ContextKeyUnitEnum::Locale, fn ($value) => $value . '-US');
109+
110+
$this->assertSame('en-US', $result);
111+
$this->assertSame('en-US', Context::get(ContextKeyUnitEnum::Locale));
112+
}
113+
114+
public function testGetOrSetWithBackedEnum(): void
115+
{
116+
// First call should set and return the value
117+
$result = Context::getOrSet(ContextKeyBackedEnum::RequestId, 'req-001');
118+
$this->assertSame('req-001', $result);
119+
120+
// Second call should return existing value, not set new one
121+
$result = Context::getOrSet(ContextKeyBackedEnum::RequestId, 'req-002');
122+
$this->assertSame('req-001', $result);
123+
}
124+
125+
public function testGetOrSetWithUnitEnum(): void
126+
{
127+
$result = Context::getOrSet(ContextKeyUnitEnum::Theme, 'dark');
128+
$this->assertSame('dark', $result);
129+
130+
$result = Context::getOrSet(ContextKeyUnitEnum::Theme, 'light');
131+
$this->assertSame('dark', $result);
132+
}
133+
134+
public function testGetOrSetWithClosure(): void
135+
{
136+
$callCount = 0;
137+
$callback = function () use (&$callCount) {
138+
++$callCount;
139+
return 'computed-value';
140+
};
141+
142+
$result = Context::getOrSet(ContextKeyBackedEnum::Tenant, $callback);
143+
$this->assertSame('computed-value', $result);
144+
$this->assertSame(1, $callCount);
145+
146+
// Closure should not be called again
147+
$result = Context::getOrSet(ContextKeyBackedEnum::Tenant, $callback);
148+
$this->assertSame('computed-value', $result);
149+
$this->assertSame(1, $callCount);
150+
}
151+
152+
public function testSetManyWithEnumKeys(): void
153+
{
154+
Context::setMany([
155+
ContextKeyBackedEnum::CurrentUser->value => 'user-123',
156+
ContextKeyUnitEnum::Locale->name => 'en-US',
157+
]);
158+
159+
$this->assertSame('user-123', Context::get(ContextKeyBackedEnum::CurrentUser));
160+
$this->assertSame('en-US', Context::get(ContextKeyUnitEnum::Locale));
161+
}
162+
163+
public function testBackedEnumAndStringInteroperability(): void
164+
{
165+
// Set with enum
166+
Context::set(ContextKeyBackedEnum::CurrentUser, 'user-123');
167+
168+
// Get with string (the enum value)
169+
$this->assertSame('user-123', Context::get('current-user'));
170+
171+
// Set with string
172+
Context::set('request-id', 'req-456');
173+
174+
// Get with enum
175+
$this->assertSame('req-456', Context::get(ContextKeyBackedEnum::RequestId));
176+
}
177+
178+
public function testUnitEnumAndStringInteroperability(): void
179+
{
180+
// Set with enum
181+
Context::set(ContextKeyUnitEnum::Locale, 'en-US');
182+
183+
// Get with string (the enum name)
184+
$this->assertSame('en-US', Context::get('Locale'));
185+
186+
// Set with string
187+
Context::set('Theme', 'dark');
188+
189+
// Get with enum
190+
$this->assertSame('dark', Context::get(ContextKeyUnitEnum::Theme));
191+
}
192+
193+
public function testGetWithDefaultAndBackedEnum(): void
194+
{
195+
$result = Context::get(ContextKeyBackedEnum::CurrentUser, 'default-user');
196+
197+
$this->assertSame('default-user', $result);
198+
}
199+
200+
public function testGetWithDefaultAndUnitEnum(): void
201+
{
202+
$result = Context::get(ContextKeyUnitEnum::Locale, 'en');
203+
204+
$this->assertSame('en', $result);
205+
}
206+
207+
public function testMultipleEnumKeysCanCoexist(): void
208+
{
209+
Context::set(ContextKeyBackedEnum::CurrentUser, 'user-123');
210+
Context::set(ContextKeyBackedEnum::RequestId, 'req-456');
211+
Context::set(ContextKeyBackedEnum::Tenant, 'tenant-789');
212+
Context::set(ContextKeyUnitEnum::Locale, 'en-US');
213+
Context::set(ContextKeyUnitEnum::Theme, 'dark');
214+
215+
$this->assertSame('user-123', Context::get(ContextKeyBackedEnum::CurrentUser));
216+
$this->assertSame('req-456', Context::get(ContextKeyBackedEnum::RequestId));
217+
$this->assertSame('tenant-789', Context::get(ContextKeyBackedEnum::Tenant));
218+
$this->assertSame('en-US', Context::get(ContextKeyUnitEnum::Locale));
219+
$this->assertSame('dark', Context::get(ContextKeyUnitEnum::Theme));
220+
}
221+
}

0 commit comments

Comments
 (0)