diff --git a/composer.json b/composer.json index 8a178bd9..76a5f595 100644 --- a/composer.json +++ b/composer.json @@ -55,6 +55,7 @@ "brick/math": "^0.9|^0.10|^0.11|^0.12", "paragonie/constant_time_encoding": "^2.6", "paragonie/sodium_compat": "^1.20", + "psr/cache": "^3.0", "psr/clock": "^1.0", "psr/event-dispatcher": "^1.0", "psr/http-client": "^1.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a5104296..adbac08c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2043,6 +2043,11 @@ parameters: count: 1 path: src/Library/KeyManagement/KeyConverter/RSAKey.php + - + message: "#^Method Jose\\\\Component\\\\KeyManagement\\\\UrlKeySetFactory\\:\\:getContent\\(\\) should return string but returns mixed\\.$#" + count: 1 + path: src/Library/KeyManagement/UrlKeySetFactory.php + - message: "#^Cannot cast mixed to string\\.$#" count: 1 diff --git a/src/Library/KeyManagement/UrlKeySetFactory.php b/src/Library/KeyManagement/UrlKeySetFactory.php index 6bd2553c..ede4d62d 100644 --- a/src/Library/KeyManagement/UrlKeySetFactory.php +++ b/src/Library/KeyManagement/UrlKeySetFactory.php @@ -4,9 +4,11 @@ namespace Jose\Component\KeyManagement; +use Psr\Cache\CacheItemPoolInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use RuntimeException; +use Symfony\Component\Cache\Adapter\NullAdapter; use Symfony\Contracts\HttpClient\HttpClientInterface; use function assert; @@ -15,6 +17,10 @@ */ abstract class UrlKeySetFactory { + private CacheItemPoolInterface $cacheItemPool; + + private int $expiresAfter = 3600; + public function __construct( private readonly ClientInterface|HttpClientInterface $client, private readonly null|RequestFactoryInterface $requestFactory = null @@ -35,6 +41,13 @@ public function __construct( ClientInterface::class )); } + $this->cacheItemPool = new NullAdapter(); + } + + public function enabledCache(CacheItemPoolInterface $cacheItemPool, int $expiresAfter = 3600): void + { + $this->cacheItemPool = $cacheItemPool; + $this->expiresAfter = $expiresAfter; } /** @@ -42,10 +55,22 @@ public function __construct( */ protected function getContent(string $url, array $header = []): string { - if ($this->client instanceof HttpClientInterface) { - return $this->sendSymfonyRequest($url, $header); + $cacheKey = hash('xxh128', $url); + $item = $this->cacheItemPool->getItem($cacheKey); + if ($item->isHit()) { + return $item->get(); } - return $this->sendPsrRequest($url, $header); + + $content = $this->client instanceof HttpClientInterface ? $this->sendSymfonyRequest( + $url, + $header + ) : $this->sendPsrRequest($url, $header); + $item = $this->cacheItemPool->getItem($cacheKey); + $item->expiresAfter($this->expiresAfter); + $item->set($content); + $this->cacheItemPool->save($item); + + return $content; } /** diff --git a/src/Library/composer.json b/src/Library/composer.json index 0f717183..271a683f 100644 --- a/src/Library/composer.json +++ b/src/Library/composer.json @@ -44,6 +44,7 @@ "brick/math": "^0.9|^0.10|^0.11|^0.12", "paragonie/constant_time_encoding": "^2.6", "paragonie/sodium_compat": "^1.20", + "psr/cache": "^3.0", "psr/clock": "^1.0", "psr/http-factory": "^1.0", "psr/http-client": "^1.0",