Skip to content

Commit faefa42

Browse files
authored
feat: add support for scalar key types in memoization methods (#3)
- Accept string|int|float|null keys in memo(), forget(), and has() methods - Automatically convert numeric keys to string internally for consistency
1 parent 2c72a78 commit faefa42

4 files changed

Lines changed: 112 additions & 17 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ You can also not use namespaces and just memoize keys.
9090
// Expensive API call cached by key
9191
$weather = memoize()->memo(
9292
'weather_london',
93-
fn() => Http::get('api.weather.com/london'->json()
93+
fn() => Http::get('api.weather.com/london')->json()
9494
);
9595

9696
// Database query with dynamic key
@@ -286,7 +286,7 @@ $salesReport = memoize()->memo(
286286
<tr><td width="30%"><strong>Method</strong></td><td><strong>Description</strong></td></tr>
287287
<tr><td>
288288

289-
**memo(?string $key, callable $callback)**
289+
**memo(string|int|float|null $key, callable $callback)**
290290

291291
</td><td>
292292

@@ -319,12 +319,12 @@ $salesReport = memoize()->memo(
319319
<tr><td width="30%"><strong>Method</strong></td><td><strong>Description</strong></td></tr>
320320
<tr><td>
321321

322-
**has(string $key): bool**
322+
**has(string|int|float $key): bool**
323323

324324
</td><td>Check if a key exists in cache</td></tr>
325325
<tr><td>
326326

327-
**forget(string $key): bool**
327+
**forget(string|int|float $key): bool**
328328

329329
</td><td>Remove specific key from cache</td></tr>
330330
<tr><td>

src/Services/MemoizeManager.php

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,26 @@ public function for(string $class): self
6666
* This prevents repeated cache hits within the same execution, significantly
6767
* improving performance.
6868
*
69-
* @param string|null $key Cache key (null returns null when namespace is set)
69+
* @param string|int|float|null $key Cache key (null returns null when namespace is set)
7070
* @param callable $callback Function to execute if value is not memoized
7171
*/
72-
public function memo(?string $key, callable $callback): mixed
72+
public function memo(string|int|float|null $key, callable $callback): mixed
7373
{
7474
// Handle null key with namespace: return null without executing callback
75-
if ($key === null && $this->namespace !== null) {
75+
if (($key === null || $key === '') && $this->namespace !== null) {
7676
$this->namespace = null;
7777

7878
return null;
7979
}
8080

8181
// Handle null key without namespace: throw exception (backward compatibility)
82-
if ($key === null) {
82+
if ($key === null || $key === '') {
8383
throw new InvalidArgumentException('Key cannot be null when no namespace is set');
8484
}
8585

86+
// Convert key to string for consistent handling
87+
$key = (string) $key;
88+
8689
// Build namespaced key if namespace is set
8790
$namespacedKey = $this->buildNamespacedKey($this->namespace, $key);
8891

@@ -120,12 +123,12 @@ public function memo(?string $key, callable $callback): mixed
120123
/**
121124
* Remove a specific memoized value by key.
122125
*
123-
* @param string $key Cache key to remove
126+
* @param string|int|float $key Cache key to remove
124127
* @return bool True if the key existed and was removed, false otherwise
125128
*/
126-
public function forget(string $key): bool
129+
public function forget(string|int|float $key): bool
127130
{
128-
$namespacedKey = $this->buildNamespacedKey($this->namespace, $key);
131+
$namespacedKey = $this->buildNamespacedKey($this->namespace, (string) $key);
129132

130133
if (array_key_exists($namespacedKey, $this->memoizedValues)) {
131134

@@ -159,12 +162,12 @@ public function flush(): void
159162
/**
160163
* Check if a key exists in the memoized cache.
161164
*
162-
* @param string $key Cache key to check
165+
* @param string|int|float $key Cache key to check
163166
* @return bool True if the key exists, false otherwise
164167
*/
165-
public function has(string $key): bool
168+
public function has(string|int|float $key): bool
166169
{
167-
$namespacedKey = $this->buildNamespacedKey($this->namespace, $key);
170+
$namespacedKey = $this->buildNamespacedKey($this->namespace, (string) $key);
168171

169172
$result = array_key_exists($namespacedKey, $this->memoizedValues);
170173

src/Support/Facades/Memoize.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
use Tomloprod\Memoize\Services\MemoizeManager;
99

1010
/**
11-
* @method static mixed memo(?string $key, callable $callback)
12-
* @method static bool forget(string $key)
11+
* @method static mixed memo(string|int|float|null $key, callable $callback)
12+
* @method static bool forget(string|int|float $key)
1313
* @method static void flush()
14-
* @method static bool has(string $key)
14+
* @method static bool has(string|int|float $key)
1515
* @method static array<string, MemoEntry> getMemoizedValues()
1616
* @method static callable once(callable $fn)
1717
* @method static void setMaxSize(?int $maxSize)

tests/Services/MemoizeManagerTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,3 +775,95 @@
775775
expect($entries)->toHaveKey('App\\Models\\User::key2');
776776
expect($entries)->toHaveKey('App\\Models\\User::key3');
777777
});
778+
779+
test('memo works with integer keys', function (): void {
780+
$manager = MemoizeManager::instance();
781+
$manager->flush();
782+
783+
$callCount = 0;
784+
$callback = function () use (&$callCount): string {
785+
$callCount++;
786+
787+
return 'result_'.$callCount;
788+
};
789+
790+
// Test with positive integer
791+
$result1 = $manager->memo(123, $callback);
792+
expect($result1)->toBe('result_1');
793+
expect($callCount)->toBe(1);
794+
795+
// Should return memoized value
796+
$result2 = $manager->memo(123, $callback);
797+
expect($result2)->toBe('result_1');
798+
expect($callCount)->toBe(1);
799+
800+
// Test with negative integer
801+
$result3 = $manager->memo(-456, $callback);
802+
expect($result3)->toBe('result_2');
803+
expect($callCount)->toBe(2);
804+
805+
// Verify keys are stored as strings
806+
$entries = $manager->getMemoizedValues();
807+
expect($entries)->toHaveKey('123');
808+
expect($entries)->toHaveKey('-456');
809+
});
810+
811+
test('memo works with float keys', function (): void {
812+
$manager = MemoizeManager::instance();
813+
$manager->flush();
814+
815+
$callCount = 0;
816+
$callback = function () use (&$callCount): string {
817+
$callCount++;
818+
819+
return 'result_'.$callCount;
820+
};
821+
822+
// Test with positive float
823+
$result1 = $manager->memo(12.34, $callback);
824+
expect($result1)->toBe('result_1');
825+
expect($callCount)->toBe(1);
826+
827+
// Should return memoized value
828+
$result2 = $manager->memo(12.34, $callback);
829+
expect($result2)->toBe('result_1');
830+
expect($callCount)->toBe(1);
831+
832+
// Verify keys are stored as strings
833+
$entries = $manager->getMemoizedValues();
834+
expect($entries)->toHaveKey('12.34');
835+
});
836+
837+
test('different types with same string representation share same cache entry', function (): void {
838+
$manager = MemoizeManager::instance();
839+
$manager->flush();
840+
841+
// These should be the same entry since both convert to '123'
842+
$manager->memo(123, fn (): string => 'integer_value');
843+
$manager->memo('123', fn (): string => 'string_value');
844+
845+
// Should be only 1 entry since both convert to '123'
846+
expect($manager->getMemoizedValues())->toHaveCount(1);
847+
expect($manager->memo(123, fn (): string => 'should_not_execute'))->toBe('integer_value');
848+
expect($manager->memo('123', fn (): string => 'should_not_execute'))->toBe('integer_value');
849+
});
850+
851+
test('forget and has work with numeric keys', function (): void {
852+
$manager = MemoizeManager::instance();
853+
$manager->flush();
854+
855+
$manager->memo(123, fn (): string => 'int_value');
856+
$manager->memo(45.67, fn (): string => 'float_value');
857+
858+
expect($manager->has(123))->toBe(true);
859+
expect($manager->has(45.67))->toBe(true);
860+
expect($manager->has('123'))->toBe(true); // Same as int 123
861+
expect($manager->has('45.67'))->toBe(true); // Same as float 45.67
862+
863+
expect($manager->forget(123))->toBe(true);
864+
expect($manager->has(123))->toBe(false);
865+
expect($manager->has('123'))->toBe(false); // Also false since it's the same key
866+
867+
expect($manager->forget(45.67))->toBe(true);
868+
expect($manager->has(45.67))->toBe(false);
869+
});

0 commit comments

Comments
 (0)