Skip to content

Commit 7ddf5e3

Browse files
authored
fix: resolve cache value display issue across all drivers (Redis, File, Database) (#3)
* fix: resolve cache value display issue across all drivers (Redis, File, Database) Fix cache:list command showing (null) values by implementing driver-specific fallback logic. Added proper prefix handling for Redis, file content parsing for File driver, and universal error handling for all cache drivers. * add fix * Remove value preview functionality and enhance test coverage - Remove getValuePreview() method and related value display functionality - Remove ValuePreviewTest.php and related test cases - Simplify command output to show only cache keys without values - Add comprehensive unit tests for CacheUiLaravelCommand private methods - Add tests for CacheUiLaravelServiceProvider and CacheUiLaravel Facade - Improve test coverage across all package components - Update command interface to focus on key listing and deletion only
1 parent 9bcdc73 commit 7ddf5e3

File tree

8 files changed

+323
-118
lines changed

8 files changed

+323
-118
lines changed

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
os: [ubuntu-latest]
2424
php: [8.3]
2525
laravel: [12.*]
26-
stability: [prefer-lowest, prefer-stable]
26+
stability: [prefer-stable]
2727
include:
2828
- laravel: 12.*
2929
testbench: 10.*

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Are you sure you want to delete this cache key? › No / Yes
103103
🗑️ The key 'user_1_profile' has been successfully deleted
104104
```
105105
106-
### Programmatic Usage
106+
### Programmatic Usage (optional)
107107
108108
You can also use the `CacheUiLaravel` class directly in your code:
109109

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"minimum-stability": "stable",
1818
"require": {
1919
"php": "^8.3",
20-
"illuminate/contracts": "^12.32.5",
20+
"laravel/framework": "^12.32.5",
2121
"laravel/prompts": "^0.3.7"
2222
},
2323
"require-dev": {

src/Commands/CacheUiLaravelCommand.php

Lines changed: 128 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,8 @@ public function handle(): int
6161
return self::SUCCESS;
6262
}
6363

64-
// Get the key value to show information
65-
$value = $this->store->get($selectedKey);
66-
$valuePreview = $this->getValuePreview($value);
67-
6864
$this->newLine();
6965
$this->line("📝 <fg=cyan>Key:</> {$selectedKey}");
70-
$this->line("💾 <fg=cyan>Value:</> {$valuePreview}");
7166
$this->newLine();
7267

7368
$confirmed = confirm(
@@ -81,7 +76,23 @@ public function handle(): int
8176
return self::SUCCESS;
8277
}
8378

84-
if ($this->store->forget($selectedKey)) {
79+
// Try to delete the key
80+
// For Redis, we need to add the prefix back since we removed it when listing keys
81+
if ($this->driver === 'redis') {
82+
$prefix = config('database.redis.options.prefix', '');
83+
$fullKey = $prefix ? $prefix.$selectedKey : $selectedKey;
84+
$deleted = $this->store->forget($fullKey);
85+
} else {
86+
$deleted = $this->store->forget($selectedKey);
87+
}
88+
89+
// If not deleted, try different approaches based on driver
90+
if (! $deleted && $this->driver === 'file') {
91+
// For file driver, try to delete using the actual key
92+
$deleted = $this->deleteFileKeyByKey($selectedKey);
93+
}
94+
95+
if ($deleted) {
8596
info("🗑️ The key '{$selectedKey}' has been successfully deleted");
8697

8798
return self::SUCCESS;
@@ -119,7 +130,7 @@ private function getRedisKeys(): array
119130
return $key;
120131
}, $keys);
121132
} catch (Exception $e) {
122-
error('Error al obtener claves de Redis: '.$e->getMessage());
133+
error('Error getting Redis keys: '.$e->getMessage());
123134

124135
return [];
125136
}
@@ -138,17 +149,39 @@ private function getFileKeys(): array
138149
$keys = [];
139150

140151
foreach ($files as $file) {
141-
// El nombre del archivo en Laravel es un hash, pero podemos leer el contenido
142-
$content = File::get($file->getPathname());
143-
144-
// Formato del archivo de caché de Laravel: expiration_time + serialized_value
145-
// Intentar extraer el nombre de la clave del contenido serializado
146-
$keys[] = $file->getFilename();
152+
try {
153+
$content = File::get($file->getPathname());
154+
155+
// Laravel file cache format: expiration_time + serialized_value
156+
if (mb_strlen($content) < 10) {
157+
continue;
158+
}
159+
160+
$expiration = mb_substr($content, 0, 10);
161+
$serialized = mb_substr($content, 10);
162+
163+
// Check if expired
164+
if (time() > $expiration) {
165+
continue;
166+
}
167+
168+
// Try to unserialize to get the actual key
169+
$data = unserialize($serialized);
170+
if (is_array($data) && isset($data['key'])) {
171+
$keys[] = $data['key'];
172+
} else {
173+
// Fallback to filename if we can't extract the key
174+
$keys[] = $file->getFilename();
175+
}
176+
} catch (Exception) {
177+
// If we can't read this file, skip it
178+
continue;
179+
}
147180
}
148181

149182
return $keys;
150183
} catch (Exception $e) {
151-
error('Error al obtener claves del sistema de archivos: '.$e->getMessage());
184+
error('Error getting file system keys: '.$e->getMessage());
152185

153186
return [];
154187
}
@@ -161,7 +194,7 @@ private function getDatabaseKeys(): array
161194

162195
return DB::table($table)->pluck('key')->toArray();
163196
} catch (Exception $e) {
164-
error('Error al obtener claves de la base de datos: '.$e->getMessage());
197+
error('Error getting database keys: '.$e->getMessage());
165198

166199
return [];
167200
}
@@ -184,32 +217,95 @@ private function handleUnsupportedDriver(): array
184217
return [];
185218
}
186219

187-
private function getValuePreview(mixed $value): string
220+
private function getFileKeyValue(string $filename): mixed
188221
{
189-
$previewLimit = config('cache-ui-laravel.preview_limit', 100);
222+
try {
223+
$cachePath = config('cache.stores.file.path', storage_path('framework/cache/data'));
224+
$filePath = $cachePath.'/'.$filename;
190225

191-
if (is_null($value)) {
192-
return '<fg=gray>(null)</>';
193-
}
226+
if (! File::exists($filePath)) {
227+
return null;
228+
}
194229

195-
if (is_bool($value)) {
196-
return $value ? '<fg=green>true</>' : '<fg=red>false</>';
197-
}
230+
$content = File::get($filePath);
198231

199-
if (is_array($value) || is_object($value)) {
200-
$json = json_encode($value, JSON_UNESCAPED_UNICODE);
201-
if (mb_strlen($json) > $previewLimit) {
202-
return mb_substr($json, 0, $previewLimit).'<fg=gray>...</>';
232+
// Laravel file cache format: expiration_time + serialized_value
233+
if (mb_strlen($content) < 10) {
234+
return null;
203235
}
204236

205-
return $json;
237+
$expiration = mb_substr($content, 0, 10);
238+
$serialized = mb_substr($content, 10);
239+
240+
// Check if expired
241+
if (time() > $expiration) {
242+
return null;
243+
}
244+
245+
return unserialize($serialized);
246+
} catch (Exception) {
247+
return null;
206248
}
249+
}
250+
251+
private function deleteFileKeyByKey(string $key): bool
252+
{
253+
try {
254+
$cachePath = config('cache.stores.file.path', storage_path('framework/cache/data'));
255+
256+
if (! File::exists($cachePath)) {
257+
return false;
258+
}
207259

208-
$stringValue = (string) $value;
209-
if (mb_strlen($stringValue) > $previewLimit) {
210-
return mb_substr($stringValue, 0, $previewLimit).'<fg=gray>...</>';
260+
$files = File::allFiles($cachePath);
261+
262+
foreach ($files as $file) {
263+
try {
264+
$content = File::get($file->getPathname());
265+
266+
// Laravel file cache format: expiration_time + serialized_value
267+
if (mb_strlen($content) < 10) {
268+
continue;
269+
}
270+
271+
$expiration = mb_substr($content, 0, 10);
272+
$serialized = mb_substr($content, 10);
273+
274+
// Check if expired
275+
if (time() > $expiration) {
276+
continue;
277+
}
278+
279+
// Try to unserialize to get the data
280+
$data = unserialize($serialized);
281+
if (is_array($data) && isset($data['key']) && $data['key'] === $key) {
282+
return File::delete($file->getPathname());
283+
}
284+
} catch (Exception) {
285+
// If we can't read this file, skip it
286+
continue;
287+
}
288+
}
289+
290+
return false;
291+
} catch (Exception) {
292+
return false;
211293
}
294+
}
212295

213-
return $stringValue;
296+
private function deleteFileKey(string $filename): bool
297+
{
298+
try {
299+
$cachePath = config('cache.stores.file.path', storage_path('framework/cache/data'));
300+
$filePath = $cachePath.'/'.$filename;
301+
302+
if (File::exists($filePath)) {
303+
return File::delete($filePath);
304+
}
305+
306+
return false;
307+
} catch (Exception) {
308+
return false;
309+
}
214310
}
215311
}

tests/Unit/CacheUiLaravelCommandSimpleTest.php

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
declare(strict_types=1);
44

55
use Abr4xas\CacheUiLaravel\Commands\CacheUiLaravelCommand;
6-
use Illuminate\Support\Facades\Config;
76

87
describe('CacheUiLaravelCommand Basic Tests', function (): void {
98
it('has correct signature and description', function (): void {
@@ -27,88 +26,6 @@
2726
});
2827
});
2928

30-
describe('getValuePreview method', function (): void {
31-
it('handles null values', function (): void {
32-
$command = new CacheUiLaravelCommand();
33-
$reflection = new ReflectionClass($command);
34-
$method = $reflection->getMethod('getValuePreview');
35-
36-
$result = $method->invoke($command, null);
37-
expect($result)->toBe('<fg=gray>(null)</>');
38-
});
39-
40-
it('handles boolean values', function (): void {
41-
$command = new CacheUiLaravelCommand();
42-
$reflection = new ReflectionClass($command);
43-
$method = $reflection->getMethod('getValuePreview');
44-
45-
$trueResult = $method->invoke($command, true);
46-
$falseResult = $method->invoke($command, false);
47-
48-
expect($trueResult)->toBe('<fg=green>true</>');
49-
expect($falseResult)->toBe('<fg=red>false</>');
50-
});
51-
52-
it('handles array values', function (): void {
53-
$command = new CacheUiLaravelCommand();
54-
$reflection = new ReflectionClass($command);
55-
$method = $reflection->getMethod('getValuePreview');
56-
57-
$array = ['key' => 'value', 'number' => 123];
58-
$result = $method->invoke($command, $array);
59-
60-
expect($result)->toBe('{"key":"value","number":123}');
61-
});
62-
63-
it('handles object values', function (): void {
64-
$command = new CacheUiLaravelCommand();
65-
$reflection = new ReflectionClass($command);
66-
$method = $reflection->getMethod('getValuePreview');
67-
68-
$object = (object) ['key' => 'value'];
69-
$result = $method->invoke($command, $object);
70-
71-
expect($result)->toBe('{"key":"value"}');
72-
});
73-
74-
it('handles string values within limit', function (): void {
75-
$command = new CacheUiLaravelCommand();
76-
$reflection = new ReflectionClass($command);
77-
$method = $reflection->getMethod('getValuePreview');
78-
79-
$shortString = 'Hello World';
80-
$result = $method->invoke($command, $shortString);
81-
82-
expect($result)->toBe('Hello World');
83-
});
84-
85-
it('handles string values exceeding limit', function (): void {
86-
Config::set('cache-ui-laravel.preview_limit', 10);
87-
88-
$command = new CacheUiLaravelCommand();
89-
$reflection = new ReflectionClass($command);
90-
$method = $reflection->getMethod('getValuePreview');
91-
92-
$longString = 'This is a very long string that exceeds the limit';
93-
$result = $method->invoke($command, $longString);
94-
95-
expect($result)->toBe('This is a <fg=gray>...</>');
96-
});
97-
98-
it('handles array values exceeding limit', function (): void {
99-
Config::set('cache-ui-laravel.preview_limit', 20);
100-
101-
$command = new CacheUiLaravelCommand();
102-
$reflection = new ReflectionClass($command);
103-
$method = $reflection->getMethod('getValuePreview');
104-
105-
$largeArray = ['very' => 'long', 'array' => 'with', 'many' => 'keys', 'and' => 'values'];
106-
$result = $method->invoke($command, $largeArray);
107-
108-
expect($result)->toContain('<fg=gray>...</>');
109-
});
110-
});
111-
11229
describe('getCacheKeys method', function (): void {
11330
it('returns array for any driver type', function (): void {
11431
$command = new CacheUiLaravelCommand();

0 commit comments

Comments
 (0)