Skip to content

Commit bcb26c2

Browse files
author
Arnaud Coolsaet
committed
Added cache bypass/renew option
1 parent c0e96ae commit bcb26c2

File tree

6 files changed

+163
-129
lines changed

6 files changed

+163
-129
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "brightfish/caching-guzzle",
33
"description": "Cache HTTP responses through Guzzle middleware",
4-
"version": "1.1.1",
4+
"version": "1.2.0",
55
"type": "library",
66
"keywords": ["guzzle", "middleware", "cache", "laravel", "http-cache"],
77
"license": "GPL-3.0-only",

readme.md

Lines changed: 55 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,104 +5,103 @@
55
[![StyleCI](https://styleci.io/repos/175029173/shield)](https://styleci.io/repos/175029173)
66
[![Packagist](https://img.shields.io/packagist/dt/brightfish/caching-guzzle?label=Total%20downloads&style=flat-square)](https://packagist.org/packages/brightfish/caching-guzzle)
77

8-
Simple caching middleware for Guzzle, works well with Laravel or with any cache system
9-
implementing the PSR-16 caching interface.
8+
Simple caching middleware for [Guzzle](https://github.com/guzzle/guzzle/), works well with [Laravel](https://github.com/laravel) or with any cache system
9+
implementing the [PSR-16 caching interface](https://www.php-fig.org/psr/psr-16/).
1010

1111
## Installation
1212
```
1313
composer require brightfish/caching-guzzle
1414
```
1515

16-
## Using the middleware
16+
## Usage
17+
The registration of the caching middleware follows [Guzzle's documentation](http://docs.guzzlephp.org/en/stable/handlers-and-middleware.html#handlers).
18+
1719
```
1820
use GuzzleHttp\Client;
1921
use GuzzleHttp\HandlerStack;
2022
use Brightfish\CachingGuzzle\Middleware;
2123
24+
/** @var \Psr\SimpleCache\CacheInterface $store */
2225
$store = app('cache')->store('database'); // Laravel
23-
$handler = new Middleware($store, 3600);
26+
27+
$handler = new Middleware($store);
28+
2429
$stack = HandlerStack::create();
30+
2531
$stack->push($handler);
32+
2633
$client = new Client([
2734
'handler' => $stack,
2835
'base_uri' => 'https://example.org/api/'
2936
]);
3037
```
3138

32-
## Making requests and retrieving from cache
39+
### Instantiation parameters
40+
Instantiating the middleware takes 3 parameters: `new Middleware($store, $ttl = 60, $log = true)`, where only `$store`, a `SimpleCache` implementation is required. `$ttl` is the default cache duration, which can be overridden by each request. And finally, if `$log` is true, cache hits will be written to Laravel's log or the `error_log` defined by PHP (see [source](https://github.com/brightfish-be/caching-guzzle/blob/c0e96ae157b4e17363eb76ee5996995fbf0bd4a5/src/Middleware.php#L168)).
41+
42+
43+
## Making requests
44+
45+
**Available options:**
46+
47+
| Option | Type | Default | Description |
48+
|:-------|------|---------|:------------|
49+
|`cache` | bool | `true` | Completely disable the cache for this request |
50+
|`cache_anew` | bool | `false` | Bypass the cache and replace it with the new response |
51+
|`cache_ttl` | int | `60` | Cache duration in seconds, use `-1` to cache forever |
52+
|`cache_key` | string | `true` | Cache key to override the default one based on the request URI (see [Cache retrieval](https://github.com/brightfish-be/caching-guzzle#cache-retrieval)) |
53+
54+
### Cache the response for 60s (default)
3355
```
34-
# This response will be cached for 60s (same as default).
3556
$response_1 = $client->get('resource', [
3657
'cache_ttl' => 60
3758
]);
38-
39-
# This response will not be cached.
59+
```
60+
### Request anew and update the cache
61+
```
62+
$response_3 = $client->post('resource/84', [
63+
'cache_anew' => false
64+
]);
65+
```
66+
### Disable caching
67+
```
4068
$response_2 = $client->post('resource/84', [
4169
'cache' => false
4270
]);
43-
44-
# This response will be cached forever with a custom key.
45-
$response_3 = $client->post('resource/84', [
71+
```
72+
### Cache forever with a custom key
73+
```
74+
$response_4 = $client->post('resource/84', [
4675
'cache_key' => 'my-key',
4776
'cache_ttl' => -1
4877
]);
78+
```
79+
If `cache_ttl` is set to `0` the response will not be cached, but, contrary to `'cache' => false`, it may be retrieved from it.
4980

81+
## Cache retrieval
82+
```
5083
# Get response_1 from cache.
5184
$cached_response_1 = $store->get('//example.org/api/resource');
5285
53-
# Get response_3 from cache.
54-
$cached_response_3 = $store->get('my-key');
86+
# Get response_4 from cache.
87+
$cached_response_4 = $store->get('my-key');
5588
```
5689

5790
## Using the wrapper
58-
Instead of manually configuring the Guzzle client and the caching middleware, it is also possible
59-
to instantiate the Client class provided in this package. This way, the binding of the middleware is done for you.
60-
```
61-
use Brightfish\CachingGuzzle\Client;
62-
63-
$client = new Client($psrCompatibleCache, [
64-
'cache_ttl' => 3600,
65-
'cache_log' => false,
66-
'base_uri' => 'https://example.org/api/'
67-
]);
68-
```
69-
70-
## Available options
71-
72-
### Per request
73-
74-
- `$cache` (bool): whether to disable the cache for this specific request.
75-
- `$cache_ttl` (int): cache duration in seconds for this response, use `-1` to cache forever.
76-
- `$cache_key` (string): custom cache key to override the default one based on the request URI.
77-
78-
```
79-
$response_1 = $client->get('resource', [
80-
'cache' => false,
81-
'cache_ttl' => 3600
82-
]);
83-
```
84-
85-
### When instantiating the middleware
86-
87-
- `$cache` (\Psr\SimpleCache\CacheInterface): cache handler implementation.
88-
- `$ttl` (int): default cache duration in seconds [default: 60].
89-
- `$log` (bool): whether to log the cache requests [default: false].
91+
Instead of manually configuring Guzzle's client and the caching middleware, it is also possible to instantiate the `Client` class provided by this package. This way, the binding of the middleware is done for you.
9092

9193
```
92-
$handler = new CacheMiddleware($cache, $ttl, $log);
93-
```
94-
95-
### When instantiating the wrapper, pass options along with the Guzzle ones
94+
use Brightfish\CachingGuzzle\Client;
9695
97-
- `$cache` (\Psr\SimpleCache\CacheInterface): cache handler implementation.
98-
- `$cache_ttl` (int): default cache duration in seconds [default: 60].
99-
- `$cache_log` (bool): whether to log the cache requests [default: false].
96+
/** @var \Psr\SimpleCache\CacheInterface $store */
97+
$psrCompatibleCache = new Cache();
10098
101-
```
102-
$client = new Client($cache, [
103-
'cache_ttl' => 12345,
104-
'cache_log' => true,
99+
$client = new Client($psrCompatibleCache, [
100+
'cache_ttl' => 60, // default cache duration
101+
'cache_log' => false, // log the cache hits
102+
// Guzzle options:
105103
'base_uri' => 'https://example.org/api/'
104+
// ...
106105
]);
107106
```
108107

src/Client.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class Client extends GuzzleClient
1616
{
1717
/**
1818
* Create a middleware stack and instantiate Guzzle.
19-
* {@inheritdoc}
20-
* @param array $config
19+
* {@inheritDoc}
20+
* @param CacheInterface $cache
2121
*/
2222
public function __construct(CacheInterface $cache, array $config = [])
2323
{
@@ -30,7 +30,13 @@ public function __construct(CacheInterface $cache, array $config = [])
3030

3131
$config['handler']->push(new Middleware($cache, $ttl, $log));
3232

33-
unset($config['cache'], $config['cache_ttl'], $config['cache_log'], $config['cache_key']);
33+
unset(
34+
$config['cache'],
35+
$config['cache_anew'],
36+
$config['cache_ttl'],
37+
$config['cache_log'],
38+
$config['cache_key']
39+
);
3440

3541
parent::__construct($config);
3642
}

src/Middleware.php

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ class Middleware
3939

4040
/**
4141
* Cache the laravel cache driver instance.
42-
* @param CacheInterface $cache Cache handler implementation
43-
* @param int $ttl Default cache duration in seconds
44-
* @param bool $log Whether to log the cache requests
42+
* @param CacheInterface $cache Cache handler implementation
43+
* @param int $ttl Default cache duration in seconds
44+
* @param bool $log Whether to log the cache requests
4545
*/
4646
public function __construct(CacheInterface $cache, int $ttl = 60, bool $log = false)
4747
{
@@ -58,11 +58,19 @@ public function __construct(CacheInterface $cache, int $ttl = 60, bool $log = fa
5858
public function __invoke(callable $handler): callable
5959
{
6060
return function (RequestInterface $request, array $options) use (&$handler) {
61+
# By default caching is allowed, else return early
62+
if (!($options['cache'] ?? true)) {
63+
return $handler($request, $options);
64+
}
65+
6166
# Create the cache key
62-
$key = $this->makeKey($options, $request->getUri());
67+
$key = $this->getKey($request->getUri(), $options['cache_key'] ?? '');
68+
69+
# Should we bypass current cached value?
70+
$bypass = $options['cache_anew'] ?? false;
6371

6472
# Try to get from cache
65-
if ($key && $entry = $this->get($key)) {
73+
if (!$bypass && $entry = $this->get($key)) {
6674
return $entry;
6775
}
6876

@@ -71,7 +79,7 @@ public function __invoke(callable $handler): callable
7179

7280
return $promise->then(
7381
function (ResponseInterface $response) use ($options, $key) {
74-
if ($key && $ttl = $this->getTTL($options)) {
82+
if ($ttl = $this->getTTL($options)) {
7583
$this->save($key, $response, $ttl);
7684
}
7785

@@ -82,20 +90,14 @@ function (ResponseInterface $response) use ($options, $key) {
8290
}
8391

8492
/**
85-
* Create the key which will reference the cache entry.
86-
* @param array $options
93+
* Either return the custom passed key or the request URL minus the protocol.
8794
* @param UriInterface $uri
95+
* @param string $key
8896
* @return string
8997
*/
90-
protected function makeKey(array $options, UriInterface $uri): string
98+
protected function getKey(UriInterface $uri, string $key = ''): string
9199
{
92-
# Does the specific request allow caching?
93-
$cache = $options['cache'] ?? true;
94-
95-
# Either return the custom passed key or the request URL minus the protocol.
96-
return $cache
97-
? ($options['cache_key'] ?? preg_replace('#(https?:)#', '', (string)$uri))
98-
: '';
100+
return $key ?: preg_replace('#(https?:)#', '', (string)$uri);
99101
}
100102

101103
/**

tests/Feature/MiddlewareFeatureTest.php

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,14 @@ class MiddlewareFeatureTest extends FeatureTestCase
1717
const TEST_URL = 'https://duckduckgo.com/';
1818

1919
/** @var string */
20-
const TEST_STR = 'I\'m a duck!';
20+
const TEST_STR_1 = 'I\'m a duck!';
21+
22+
const TEST_STR_2 = 'I\'m a duck too!';
2123

2224
public function test_caching()
2325
{
24-
$middleware = new Middleware($this->store, 3600);
25-
26-
$mock = new MockHandler([
27-
new Response(200, [], static::TEST_STR),
28-
new Response(200, [], static::TEST_STR)
29-
]);
30-
31-
$stack = HandlerStack::create($mock);
32-
$stack->push($middleware);
33-
34-
$client = new Client([
35-
'handler' => $stack
26+
$client = $this->getClientWithMockResponses([
27+
static::TEST_STR_1,
3628
]);
3729

3830
$client->get(static::TEST_URL);
@@ -41,33 +33,45 @@ public function test_caching()
4133

4234
$cached = $this->store->get($key);
4335

44-
$this->assertStringContainsString(static::TEST_STR, $cached);
36+
$this->assertStringContainsString(static::TEST_STR_1, $cached);
37+
}
4538

46-
# Request with custom key
39+
public function test_caching_with_custom_key()
40+
{
41+
$client = $this->getClientWithMockResponses([
42+
static::TEST_STR_2,
43+
]);
4744

4845
$client->get(static::TEST_URL, ['cache_key' => 'test_key']);
4946

5047
$cached = $this->store->get('test_key');
5148

52-
$this->assertStringContainsString(static::TEST_STR, $cached);
53-
54-
// echo PHP_EOL . 'MiddlewareFeatureTest response: ' . $cached . PHP_EOL;
49+
$this->assertStringContainsString(static::TEST_STR_2, $cached);
5550
}
5651

57-
public function test_caching_with_promised_request()
52+
public function test_caching_anew()
5853
{
59-
$middleware = new Middleware($this->store, 3600);
54+
$client = $this->getClientWithMockResponses([
55+
static::TEST_STR_1,
56+
static::TEST_STR_2,
57+
]);
6058

61-
$mock = new MockHandler([
62-
new Response(200, [], static::TEST_STR),
63-
new Response(200, [], static::TEST_STR)
59+
$client->get(static::TEST_URL);
60+
61+
$client->get(static::TEST_URL, [
62+
'cache_anew' => true,
63+
'cache_key' => 'test_key',
6464
]);
6565

66-
$stack = HandlerStack::create($mock);
67-
$stack->push($middleware);
66+
$cached = $this->store->get('test_key');
6867

69-
$client = new Client([
70-
'handler' => $stack
68+
$this->assertStringContainsString(static::TEST_STR_2, $cached);
69+
}
70+
71+
public function test_caching_with_promised_request()
72+
{
73+
$client = $this->getClientWithMockResponses([
74+
static::TEST_STR_1,
7175
]);
7276

7377
$promises = [
@@ -78,7 +82,7 @@ public function test_caching_with_promised_request()
7882

7983
$this->assertEquals(
8084
(string)$responses['prom_1']->getBody(),
81-
static::TEST_STR
85+
static::TEST_STR_1
8286
);
8387
}
8488
}

0 commit comments

Comments
 (0)