Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ $location = $fileCache->get("lat-lon", $lookup);
echo "Your location is: $location";
```

If generating a fresh value fails, throw `Gt\FileCache\CacheValueGenerationException`
from the callback. The cache will ignore that failure and skip writing a replacement
value, which leaves `null` available as a legitimate cached value.

# Proudly sponsored by

[JetBrains Open Source sponsorship program](https://www.jetbrains.com/community/opensource/)
Expand Down
21 changes: 20 additions & 1 deletion example/01-latlon.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,26 @@
function httpJson(string $uri):object {
$ch = curl_init($uri);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
return json_decode(curl_exec($ch));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

$response = curl_exec($ch);
if($response === false) {
throw new Gt\FileCache\CacheValueGenerationException(curl_error($ch));
}

try {
return json_decode($response, flags: JSON_THROW_ON_ERROR);
}
catch(JsonException $exception) {
throw new Gt\FileCache\CacheValueGenerationException(
"Invalid JSON returned from $uri",
previous: $exception,
);
}
finally {
curl_close($ch);
}
}

$ipAddress = $fileCache->get("ip", function():string {
Expand Down
11 changes: 8 additions & 3 deletions src/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@
return $this->fileAccess->getData($name);
}
catch(FileNotFoundException|CacheInvalidException) {
$value = $callback();
$this->fileAccess->setData($name, $value);
return $value;
try {
$value = $callback();
$this->fileAccess->setData($name, $value);
return $value;
}
catch(CacheValueGenerationException) {

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.5)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.5)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Caught class Gt\FileCache\CacheValueGenerationException not found.

Check failure on line 37 in src/Cache.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Caught class Gt\FileCache\CacheValueGenerationException not found.
return null;
}
}
}

Expand Down
49 changes: 49 additions & 0 deletions test/phpunit/CacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
namespace Gt\FileCache\Test;

use Gt\FileCache\Cache;
use Gt\FileCache\CacheInvalidException;
use Gt\FileCache\CacheValueGenerationException;
use Gt\FileCache\FileAccess;
use Gt\FileCache\FileNotFoundException;
use PHPUnit\Framework\TestCase;
use SplFileInfo;
use SplFixedArray;
Expand Down Expand Up @@ -49,6 +52,52 @@ public function testGet_multipleCallsDoesNotCallbackMultipleTimes():void {
self::assertSame(1, $count);
}

public function testGet_nullValueCanBeCached():void {
$sut = $this->getSut();
$count = 0;

$callback = function()use(&$count):null {
$count++;
return null;
};

self::assertNull($sut->get("test-null", $callback));
self::assertNull($sut->get("test-null", $callback));
self::assertSame(1, $count);
}

public function testGet_generationExceptionDoesNotWriteInvalidValue():void {
$fileAccess = self::createMock(FileAccess::class);
$fileAccess->expects(self::once())
->method("checkValidity")
->with("test", 3600)
->willThrowException(new FileNotFoundException("test"));
$fileAccess->expects(self::never())
->method("setData");

$sut = new Cache(fileAccess: $fileAccess);

self::assertNull($sut->get("test", function():never {
throw new CacheValueGenerationException("Lookup failed");
}));
}

public function testGet_invalidCache_generationExceptionDoesNotWriteReplacement():void {
$fileAccess = self::createMock(FileAccess::class);
$fileAccess->expects(self::once())
->method("checkValidity")
->with("test", 3600)
->willThrowException(new CacheInvalidException("test"));
$fileAccess->expects(self::never())
->method("setData");

$sut = new Cache(fileAccess: $fileAccess);

self::assertNull($sut->get("test", function():never {
throw new CacheValueGenerationException("Lookup failed");
}));
}

public function testGetString():void {
$value = uniqid();
$sut = $this->getSut([
Expand Down
Loading