Skip to content

Commit a16a88b

Browse files
authored
[12.x] Add typed getters on Cache (#58451)
* typed getters on cache * wip * Handle numeric strings in typed cache getters
1 parent dcbfc2f commit a16a88b

3 files changed

Lines changed: 269 additions & 0 deletions

File tree

src/Illuminate/Cache/Repository.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Illuminate\Support\Collection;
2828
use Illuminate\Support\InteractsWithTime;
2929
use Illuminate\Support\Traits\Macroable;
30+
use InvalidArgumentException;
3031

3132
use function Illuminate\Support\defer;
3233
use function Illuminate\Support\enum_value;
@@ -215,6 +216,124 @@ public function pull($key, $default = null)
215216
});
216217
}
217218

219+
/**
220+
* Retrieve a string item from the cache.
221+
*
222+
* @param \BackedEnum|\UnitEnum|string $key
223+
* @param (\Closure():(string|null))|string|null $default
224+
* @return string
225+
*
226+
* @throws \InvalidArgumentException
227+
*/
228+
public function string($key, $default = null): string
229+
{
230+
$value = $this->get($key, $default);
231+
232+
if (! is_string($value)) {
233+
throw new InvalidArgumentException(
234+
sprintf('Cache value for key [%s] must be a string, %s given.', $key, gettype($value))
235+
);
236+
}
237+
238+
return $value;
239+
}
240+
241+
/**
242+
* Retrieve an integer item from the cache.
243+
*
244+
* @param \BackedEnum|\UnitEnum|string $key
245+
* @param (\Closure():(int|null))|int|null $default
246+
* @return int
247+
*
248+
* @throws \InvalidArgumentException
249+
*/
250+
public function integer($key, $default = null): int
251+
{
252+
$value = $this->get($key, $default);
253+
254+
if (is_int($value)) {
255+
return $value;
256+
}
257+
258+
if (filter_var($value, FILTER_VALIDATE_INT) !== false) {
259+
return (int) $value;
260+
}
261+
262+
throw new InvalidArgumentException(
263+
sprintf('Cache value for key [%s] must be an integer, %s given.', $key, gettype($value))
264+
);
265+
}
266+
267+
/**
268+
* Retrieve a float item from the cache.
269+
*
270+
* @param \BackedEnum|\UnitEnum|string $key
271+
* @param (\Closure():(float|null))|float|null $default
272+
* @return float
273+
*
274+
* @throws \InvalidArgumentException
275+
*/
276+
public function float($key, $default = null): float
277+
{
278+
$value = $this->get($key, $default);
279+
280+
if (is_float($value)) {
281+
return $value;
282+
}
283+
284+
if (filter_var($value, FILTER_VALIDATE_FLOAT) !== false) {
285+
return (float) $value;
286+
}
287+
288+
throw new InvalidArgumentException(
289+
sprintf('Cache value for key [%s] must be a float, %s given.', $key, gettype($value))
290+
);
291+
}
292+
293+
/**
294+
* Retrieve a boolean item from the cache.
295+
*
296+
* @param \BackedEnum|\UnitEnum|string $key
297+
* @param (\Closure():(bool|null))|bool|null $default
298+
* @return bool
299+
*
300+
* @throws \InvalidArgumentException
301+
*/
302+
public function boolean($key, $default = null): bool
303+
{
304+
$value = $this->get($key, $default);
305+
306+
if (! is_bool($value)) {
307+
throw new InvalidArgumentException(
308+
sprintf('Cache value for key [%s] must be a boolean, %s given.', $key, gettype($value))
309+
);
310+
}
311+
312+
return $value;
313+
}
314+
315+
/**
316+
* Retrieve an array item from the cache.
317+
*
318+
* @param \BackedEnum|\UnitEnum|string $key
319+
* @param (\Closure():(array<array-key, mixed>|null))|array<array-key, mixed>|null $default
320+
* @return array<array-key, mixed>
321+
*
322+
* @throws \InvalidArgumentException
323+
*/
324+
public function array($key, $default = null): array
325+
{
326+
$value = $this->get($key, $default);
327+
328+
if (! is_array($value)) {
329+
throw new InvalidArgumentException(
330+
sprintf('Cache value for key [%s] must be an array, %s given.', $key, gettype($value))
331+
);
332+
}
333+
334+
return $value;
335+
}
336+
218337
/**
219338
* Store an item in the cache.
220339
*

src/Illuminate/Support/Facades/Cache.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
* @method static array many(array $keys)
2525
* @method static iterable getMultiple(iterable $keys, mixed $default = null)
2626
* @method static mixed pull(\BackedEnum|\UnitEnum|array|string $key, mixed $default = null)
27+
* @method static string string(\BackedEnum|\UnitEnum|string $key, \Closure|string|null $default = null)
28+
* @method static int integer(\BackedEnum|\UnitEnum|string $key, \Closure|int|null $default = null)
29+
* @method static float float(\BackedEnum|\UnitEnum|string $key, \Closure|float|null $default = null)
30+
* @method static bool boolean(\BackedEnum|\UnitEnum|string $key, \Closure|bool|null $default = null)
31+
* @method static array array(\BackedEnum|\UnitEnum|string $key, \Closure|array|null $default = null)
2732
* @method static bool put(\BackedEnum|\UnitEnum|array|string $key, mixed $value, \DateTimeInterface|\DateInterval|int|null $ttl = null)
2833
* @method static bool set(\BackedEnum|\UnitEnum|array|string $key, mixed $value, \DateTimeInterface|\DateInterval|int|null $ttl = null)
2934
* @method static bool putMany(array $values, \DateTimeInterface|\DateInterval|int|null $ttl = null)

tests/Cache/CacheRepositoryTest.php

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Illuminate\Events\Dispatcher;
2222
use Illuminate\Filesystem\Filesystem;
2323
use Illuminate\Support\Carbon;
24+
use InvalidArgumentException;
2425
use Mockery as m;
2526
use PHPUnit\Framework\Attributes\DataProvider;
2627
use PHPUnit\Framework\TestCase;
@@ -516,4 +517,148 @@ protected static function getTestDate()
516517
{
517518
return '2030-07-25 12:13:14 UTC';
518519
}
520+
521+
public function testItGetsAsString()
522+
{
523+
$repo = $this->getRepository();
524+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
525+
$this->assertSame('bar', $repo->string('foo'));
526+
}
527+
528+
public function testItGetsAsStringWithDefault()
529+
{
530+
$repo = $this->getRepository();
531+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
532+
$this->assertSame('default', $repo->string('foo', 'default'));
533+
}
534+
535+
public function testItThrowsExceptionWhenGettingNonStringAsString()
536+
{
537+
$this->expectException(InvalidArgumentException::class);
538+
$this->expectExceptionMessage('Cache value for key [foo] must be a string, integer given.');
539+
540+
$repo = $this->getRepository();
541+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(123);
542+
$repo->string('foo');
543+
}
544+
545+
public function testItGetsAsInteger()
546+
{
547+
$repo = $this->getRepository();
548+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(123);
549+
$this->assertSame(123, $repo->integer('foo'));
550+
}
551+
552+
public function testItGetsAsIntegerWithDefault()
553+
{
554+
$repo = $this->getRepository();
555+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
556+
$this->assertSame(456, $repo->integer('foo', 456));
557+
}
558+
559+
public function testItGetsAsIntegerFromNumericString()
560+
{
561+
$repo = $this->getRepository();
562+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('123');
563+
$this->assertSame(123, $repo->integer('foo'));
564+
}
565+
566+
public function testItThrowsExceptionWhenGettingNonIntegerAsInteger()
567+
{
568+
$this->expectException(InvalidArgumentException::class);
569+
$this->expectExceptionMessage('Cache value for key [foo] must be an integer, string given.');
570+
571+
$repo = $this->getRepository();
572+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
573+
$repo->integer('foo');
574+
}
575+
576+
public function testItThrowsExceptionWhenGettingFloatStringAsInteger()
577+
{
578+
$this->expectException(InvalidArgumentException::class);
579+
$this->expectExceptionMessage('Cache value for key [foo] must be an integer, string given.');
580+
581+
$repo = $this->getRepository();
582+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('1.5');
583+
$repo->integer('foo');
584+
}
585+
586+
public function testItGetsAsFloat()
587+
{
588+
$repo = $this->getRepository();
589+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(1.5);
590+
$this->assertSame(1.5, $repo->float('foo'));
591+
}
592+
593+
public function testItGetsAsFloatWithDefault()
594+
{
595+
$repo = $this->getRepository();
596+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
597+
$this->assertSame(2.5, $repo->float('foo', 2.5));
598+
}
599+
600+
public function testItGetsAsFloatFromNumericString()
601+
{
602+
$repo = $this->getRepository();
603+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('1.5');
604+
$this->assertSame(1.5, $repo->float('foo'));
605+
}
606+
607+
public function testItThrowsExceptionWhenGettingNonFloatAsFloat()
608+
{
609+
$this->expectException(InvalidArgumentException::class);
610+
$this->expectExceptionMessage('Cache value for key [foo] must be a float, string given.');
611+
612+
$repo = $this->getRepository();
613+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
614+
$repo->float('foo');
615+
}
616+
617+
public function testItGetsAsBoolean()
618+
{
619+
$repo = $this->getRepository();
620+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(true);
621+
$this->assertTrue($repo->boolean('foo'));
622+
}
623+
624+
public function testItGetsAsBooleanWithDefault()
625+
{
626+
$repo = $this->getRepository();
627+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
628+
$this->assertFalse($repo->boolean('foo', false));
629+
}
630+
631+
public function testItThrowsExceptionWhenGettingNonBooleanAsBoolean()
632+
{
633+
$this->expectException(InvalidArgumentException::class);
634+
$this->expectExceptionMessage('Cache value for key [foo] must be a boolean, string given.');
635+
636+
$repo = $this->getRepository();
637+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
638+
$repo->boolean('foo');
639+
}
640+
641+
public function testItGetsAsArray()
642+
{
643+
$repo = $this->getRepository();
644+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(['bar', 'baz']);
645+
$this->assertSame(['bar', 'baz'], $repo->array('foo'));
646+
}
647+
648+
public function testItGetsAsArrayWithDefault()
649+
{
650+
$repo = $this->getRepository();
651+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null);
652+
$this->assertSame(['default'], $repo->array('foo', ['default']));
653+
}
654+
655+
public function testItThrowsExceptionWhenGettingNonArrayAsArray()
656+
{
657+
$this->expectException(InvalidArgumentException::class);
658+
$this->expectExceptionMessage('Cache value for key [foo] must be an array, string given.');
659+
660+
$repo = $this->getRepository();
661+
$repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar');
662+
$repo->array('foo');
663+
}
519664
}

0 commit comments

Comments
 (0)