Skip to content

Commit 15d6939

Browse files
authored
fix: file driver deletion bug and docs update (#10)
* fix: file driver deletion bug and docs update * verify file driver cache deletion and update README * fix style
1 parent 72f979f commit 15d6939

File tree

4 files changed

+144
-17
lines changed

4 files changed

+144
-17
lines changed

README.md

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,13 @@ CACHE_UI_PREVIEW_LIMIT=150
5050
CACHE_UI_SEARCH_SCROLL=20
5151
```
5252

53-
### Custom File Cache Driver (Recommended)
53+
### Custom File Cache Driver (Only for File Store)
5454

55-
For the best experience with file cache, you can use our custom `key-aware-file` driver that allows Cache UI to display real keys instead of file hashes.
55+
If you are using the `file` cache driver (default in Laravel), you should use our custom `key-aware-file` driver.
56+
57+
**Why?** The standard Laravel `file` driver stores keys as hashes, making them unreadable. This custom driver wraps the value to store the real key, allowing you to see and search for them.
58+
59+
> **Important**: This is **NOT** needed for Redis or Database drivers, as they support listing keys natively.
5660
5761
#### Driver Configuration
5862

@@ -84,6 +88,7 @@ namespace App\Providers;
8488
use Abr4xas\CacheUiLaravel\KeyAwareFileStore;
8589
use Illuminate\Support\Facades\Cache;
8690
use Illuminate\Support\ServiceProvider;
91+
use Illuminate\Foundation\Application;
8792

8893
class AppServiceProvider extends ServiceProvider
8994
{
@@ -101,13 +106,11 @@ class AppServiceProvider extends ServiceProvider
101106
public function boot(): void
102107
{
103108
// Register the custom file cache driver
104-
Cache::extend('key-aware-file', function ($app, $config) {
105-
return Cache::repository(new KeyAwareFileStore(
106-
$app['files'],
107-
$config['path'],
108-
$config['file_permission'] ?? null
109-
));
110-
});
109+
Cache::extend('key-aware-file', fn (Application $app, array $config) => Cache::repository(new KeyAwareFileStore(
110+
$app['files'],
111+
$config['path'],
112+
$config['file_permission'] ?? null
113+
)));
111114
}
112115
}
113116
```
@@ -156,11 +159,15 @@ php artisan cache:list --store=redis
156159

157160
### Supported Drivers
158161

159-
-**Redis**: Lists all keys using Redis KEYS command
160-
-**File**: Reads cache files from the filesystem
161-
-**Database**: Queries the cache table in the database
162-
- ⚠️ **Array**: Not supported (array driver doesn't persist between requests)
163-
- ⚠️ **Memcached**: Not currently supported
162+
| Driver | Support | Configuration Required |
163+
|--------|---------|------------------------|
164+
| **Redis** | ✅ Native | None (Works out of the box) |
165+
| **Database** | ✅ Native | None (Works out of the box) |
166+
| **File** | ✅ Enhanced | **Requires `key-aware-file` driver** |
167+
| **Array** | ⚠️ No | Not supported (doesn't persist) |
168+
| **Memcached** | ⚠️ No | Not currently supported |
169+
170+
> **Note**: The `key-aware-file` driver is **only** needed if you use the `file` cache driver. If you use Redis or Database, you don't need to change your driver configuration.
164171
165172
### Usage Example
166173

@@ -224,7 +231,7 @@ The following tests need to be implemented to fully validate the new `KeyAwareFi
224231
- [ ] Test complete cache workflow (store → retrieve → delete)
225232
- [ ] Test multiple keys with different expiration times
226233
- [ ] Test cache key listing with `getAllKeys()` method
227-
- [ ] Test cache key deletion with `forgetKey()` method
234+
- [x] Test cache key deletion with `forgetKey()` method
228235
- [ ] Test mixed wrapped and legacy data scenarios
229236
- [ ] Test performance with large numbers of cache keys
230237
@@ -237,7 +244,7 @@ The following tests need to be implemented to fully validate the new `KeyAwareFi
237244
238245
### CacheUiLaravel Integration Tests
239246
- [ ] Test `getAllKeys()` method with `key-aware-file` driver
240-
- [ ] Test `forgetKey()` method with `key-aware-file` driver
247+
- [x] Test `forgetKey()` method with `key-aware-file` driver
241248
- [ ] Test mixed driver scenarios (Redis + File + Database)
242249
- [ ] Test error handling and graceful degradation
243250

src/CacheUiLaravel.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,29 @@ public function getAllKeys(?string $store = null): array
3333
*/
3434
public function forgetKey(string $key, ?string $store = null): bool
3535
{
36+
$storeName = $store ?? config('cache.default');
3637
$cacheStore = $store !== null && $store !== '' && $store !== '0' ? Cache::store($store) : Cache::store();
3738

38-
return $cacheStore->forget($key);
39+
if ($cacheStore->forget($key)) {
40+
return true;
41+
}
42+
43+
// Handle file driver specific logic for hashed keys
44+
$driver = config("cache.stores.{$storeName}.driver");
45+
46+
if (in_array($driver, ['file', 'key-aware-file']) && preg_match('/^[a-f0-9]{40}$/', $key)) {
47+
// Use the path from the specific store configuration, fallback to default
48+
$cachePath = config("cache.stores.{$storeName}.path", config('cache.stores.file.path', storage_path('framework/cache/data')));
49+
50+
$parts = array_slice(mb_str_split($key, 2), 0, 2);
51+
$path = $cachePath.'/'.implode('/', $parts).'/'.$key;
52+
53+
if (File::exists($path)) {
54+
return File::delete($path);
55+
}
56+
}
57+
58+
return false;
3959
}
4060

4161
private function getRedisKeys(string $store): array

src/Commands/CacheUiLaravelCommand.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ public function handle(): int
9090
if (! $deleted && $this->driver === 'file') {
9191
// For file driver, try to delete using the actual key
9292
$deleted = $this->deleteFileKeyByKey($selectedKey);
93+
94+
if (! $deleted) {
95+
// If that fails, it might be a standard cache file where the key is the filename (hash)
96+
$deleted = $this->deleteFileKey($selectedKey);
97+
}
9398
}
9499

95100
if ($deleted) {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Abr4xas\CacheUiLaravel\CacheUiLaravel;
6+
use Illuminate\Support\Facades\Cache;
7+
use Illuminate\Support\Facades\Config;
8+
use Illuminate\Support\Facades\File;
9+
10+
describe('CacheUiLaravel File Driver Deletion', function (): void {
11+
beforeEach(function (): void {
12+
$this->cacheUiLaravel = new CacheUiLaravel();
13+
});
14+
15+
it('deletes hashed file when standard forget fails for file driver', function (): void {
16+
// Mock Cache::forget to return false, simulating that it couldn't find the key by name
17+
Cache::shouldReceive('store')->withNoArgs()->andReturnSelf();
18+
Cache::shouldReceive('forget')->with('008cb7ea48f292dd8b03d361a4c9f66085f77090')->andReturn(false);
19+
20+
// Configure file driver
21+
Config::set('cache.default', 'file');
22+
Config::set('cache.stores.file.driver', 'file');
23+
$cachePath = storage_path('framework/cache/data');
24+
Config::set('cache.stores.file.path', $cachePath);
25+
26+
// The key is a SHA1 hash
27+
$key = '008cb7ea48f292dd8b03d361a4c9f66085f77090';
28+
29+
// Expected file path reconstruction
30+
// 00/8c/008cb7ea48f292dd8b03d361a4c9f66085f77090
31+
$expectedPath = $cachePath.'/00/8c/'.$key;
32+
33+
// Mock File existence and deletion
34+
File::shouldReceive('exists')->with($expectedPath)->andReturn(true);
35+
File::shouldReceive('delete')->with($expectedPath)->andReturn(true);
36+
37+
$result = $this->cacheUiLaravel->forgetKey($key);
38+
39+
expect($result)->toBeTrue();
40+
});
41+
42+
it('deletes hashed file when standard forget fails for key-aware-file driver', function (): void {
43+
// Mock Cache::forget to return false
44+
Cache::shouldReceive('store')->withNoArgs()->andReturnSelf();
45+
Cache::shouldReceive('forget')->with('008cb7ea48f292dd8b03d361a4c9f66085f77090')->andReturn(false);
46+
47+
// Configure key-aware-file driver
48+
Config::set('cache.default', 'file');
49+
Config::set('cache.stores.file.driver', 'key-aware-file');
50+
$cachePath = storage_path('framework/cache/data');
51+
Config::set('cache.stores.file.path', $cachePath);
52+
53+
$key = '008cb7ea48f292dd8b03d361a4c9f66085f77090';
54+
$expectedPath = $cachePath.'/00/8c/'.$key;
55+
56+
File::shouldReceive('exists')->with($expectedPath)->andReturn(true);
57+
File::shouldReceive('delete')->with($expectedPath)->andReturn(true);
58+
59+
$result = $this->cacheUiLaravel->forgetKey($key);
60+
61+
expect($result)->toBeTrue();
62+
});
63+
64+
it('does not attempt file deletion for non-hashed keys', function (): void {
65+
Cache::shouldReceive('store')->withNoArgs()->andReturnSelf();
66+
Cache::shouldReceive('forget')->with('not-a-hash')->andReturn(false);
67+
68+
Config::set('cache.default', 'file');
69+
Config::set('cache.stores.file.driver', 'file');
70+
71+
// File::exists/delete should NOT be called
72+
File::shouldReceive('exists')->never();
73+
File::shouldReceive('delete')->never();
74+
75+
$result = $this->cacheUiLaravel->forgetKey('not-a-hash');
76+
77+
expect($result)->toBeFalse();
78+
});
79+
80+
it('does not attempt file deletion for non-file drivers', function (): void {
81+
Cache::shouldReceive('store')->withNoArgs()->andReturnSelf();
82+
Cache::shouldReceive('forget')->with('008cb7ea48f292dd8b03d361a4c9f66085f77090')->andReturn(false);
83+
84+
Config::set('cache.default', 'redis');
85+
Config::set('cache.stores.redis.driver', 'redis');
86+
87+
// File::exists/delete should NOT be called
88+
File::shouldReceive('exists')->never();
89+
File::shouldReceive('delete')->never();
90+
91+
$result = $this->cacheUiLaravel->forgetKey('008cb7ea48f292dd8b03d361a4c9f66085f77090');
92+
93+
expect($result)->toBeFalse();
94+
});
95+
});

0 commit comments

Comments
 (0)