1# guzzle-cache-middleware 2 3[![Latest Stable Version](https://poser.pugx.org/kevinrob/guzzle-cache-middleware/v/stable)](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) [![Total Downloads](https://poser.pugx.org/kevinrob/guzzle-cache-middleware/downloads)](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) [![License](https://poser.pugx.org/kevinrob/guzzle-cache-middleware/license)](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) 4[![Build Status](https://travis-ci.org/Kevinrob/guzzle-cache-middleware.svg?branch=master)](https://travis-ci.org/Kevinrob/guzzle-cache-middleware) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/077ec9d6-9362-43be-83c9-cf1db2c9c802/mini.png)](https://insight.sensiolabs.com/projects/077ec9d6-9362-43be-83c9-cf1db2c9c802) 5[![Dependency Status](https://www.versioneye.com/php/kevinrob:guzzle-cache-middleware/badge.png)](https://www.versioneye.com/php/kevinrob:guzzle-cache-middleware) [![Code Coverage](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/?branch=master) 6 7 8A HTTP Cache for [Guzzle](https://github.com/guzzle/guzzle) 6. It's a simple Middleware to be added in the HandlerStack. 9 10## Goals 11- RFC 7234 compliance 12- Performance and transparency 13- Assured compatibility with PSR-7 14 15## Storage interfaces build-in 16- [Doctrine cache](https://github.com/doctrine/cache) 17- [Laravel cache](https://laravel.com/docs/5.2/cache) 18- [Flysystem](https://github.com/thephpleague/flysystem) 19- [PSR6](https://github.com/php-fig/cache) 20- [WordPress Object Cache](https://codex.wordpress.org/Class_Reference/WP_Object_Cache) 21 22## Installation 23 24`composer require kevinrob/guzzle-cache-middleware` 25 26or add it the your `composer.json` and make a `composer update kevinrob/guzzle-cache-middleware`. 27 28# Why? 29Performance. It's very common to do some HTTP calls to an API for rendering a page and it takes times to do it. 30 31# How? 32With a simple Middleware added at the top of the `HandlerStack` of Guzzle6. 33 34```php 35use GuzzleHttp\Client; 36use GuzzleHttp\HandlerStack; 37use Kevinrob\GuzzleCache\CacheMiddleware; 38 39// Create default HandlerStack 40$stack = HandlerStack::create(); 41 42// Add this middleware to the top with `push` 43$stack->push(new CacheMiddleware(), 'cache'); 44 45// Initialize the client with the handler option 46$client = new Client(['handler' => $stack]); 47``` 48 49# Examples 50 51## Doctrine/Cache 52You can use a cache from `Doctrine/Cache`: 53```php 54[...] 55use Doctrine\Common\Cache\FilesystemCache; 56use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy; 57use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage; 58 59[...] 60$stack->push( 61 new CacheMiddleware( 62 new PrivateCacheStrategy( 63 new DoctrineCacheStorage( 64 new FilesystemCache('/tmp/') 65 ) 66 ) 67 ), 68 'cache' 69); 70``` 71 72You can use `ChainCache` for using multiple `CacheProvider`. With that provider, you have to sort the different cache from the faster to the slower. Like that, you can have a very fast cache. 73```php 74[...] 75use Doctrine\Common\Cache\ChainCache; 76use Doctrine\Common\Cache\ArrayCache; 77use Doctrine\Common\Cache\FilesystemCache; 78use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy; 79use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage; 80 81[...] 82$stack->push(new CacheMiddleware( 83 new PrivateCacheStrategy( 84 new DoctrineCacheStorage( 85 new ChainCache([ 86 new ArrayCache(), 87 new FilesystemCache('/tmp/'), 88 ]) 89 ) 90 ) 91), 'cache'); 92``` 93 94## Laravel cache 95You can use a cache with Laravel, e.g. Redis, Memcache etc.: 96```php 97[...] 98use Illuminate\Support\Facades\Cache; 99use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy; 100use Kevinrob\GuzzleCache\Storage\LaravelCacheStorage; 101 102[...] 103 104$stack->push( 105 new CacheMiddleware( 106 new PrivateCacheStrategy( 107 new LaravelCacheStorage( 108 Cache::store('redis') 109 ) 110 ) 111 ), 112 'cache' 113); 114``` 115 116## Flysystem 117```php 118[...] 119use League\Flysystem\Adapter\Local; 120use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy; 121use Kevinrob\GuzzleCache\Storage\FlysystemStorage; 122 123[...] 124 125$stack->push( 126 new CacheMiddleware( 127 new PrivateCacheStrategy( 128 new FlysystemStorage( 129 new Local('/path/to/cache') 130 ) 131 ) 132 ), 133 'cache' 134); 135``` 136 137## WordPress Object Cache 138```php 139[...] 140use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy; 141use Kevinrob\GuzzleCache\Storage\WordPressObjectCacheStorage; 142 143[...] 144 145$stack->push( 146 new CacheMiddleware( 147 new PrivateCacheStrategy( 148 new WordPressObjectCacheStorage() 149 ) 150 ), 151 'cache' 152); 153``` 154 155## Public and shared 156It's possible to add a public shared cache to the stack: 157```php 158[...] 159use Doctrine\Common\Cache\FilesystemCache; 160use Doctrine\Common\Cache\PredisCache; 161use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy; 162use Kevinrob\GuzzleCache\Strategy\PublicCacheStrategy; 163use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage; 164 165[...] 166// Private caching 167$stack->push( 168 new CacheMiddleware( 169 new PrivateCacheStrategy( 170 new DoctrineCacheStorage( 171 new FilesystemCache('/tmp/') 172 ) 173 ) 174 ), 175 'private-cache' 176); 177 178// Public caching 179$stack->push( 180 new CacheMiddleware( 181 new PublicCacheStrategy( 182 new DoctrineCacheStorage( 183 new PredisCache( 184 new Predis\Client('tcp://10.0.0.1:6379') 185 ) 186 ) 187 ) 188 ), 189 'shared-cache' 190); 191``` 192 193## Greedy caching 194In some cases servers might send insufficient or no at all caching headers. 195Using the greedy caching strategy allows defining an expiry TTL on your own while 196disregarding any possibly present caching headers: 197```php 198[...] 199use Kevinrob\GuzzleCache\KeyValueHttpHeader; 200use Kevinrob\GuzzleCache\Strategy\GreedyCacheStrategy; 201use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage; 202use Doctrine\Common\Cache\FilesystemCache; 203 204[...] 205// Greedy caching 206$stack->push( 207 new CacheMiddleware( 208 new GreedyCacheStrategy( 209 new DoctrineCacheStorage( 210 new FilesystemCache('/tmp/') 211 ), 212 1800, // the TTL in seconds 213 new KeyValueHttpHeader(['Authorization']) // Optionnal - specify the headers that can change the cache key 214 ) 215 ), 216 'greedy-cache' 217); 218``` 219 220## Delegate caching 221Because your client may call different apps, on different domains, you may need to define which strategy is suitable to your requests. 222 223To solve this, all you have to do is to define a default cache strategy, and override it by implementing your own Request Matchers. 224 225Here's an example: 226```php 227namespace App\RequestMatcher; 228 229use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface; 230use Psr\Http\Message\RequestInterface; 231 232class ExampleOrgRequestMatcher implements RequestMatcherInterface 233{ 234 235 /** 236 * @inheritDoc 237 */ 238 public function matches(RequestInterface $request) 239 { 240 return false !== strpos($request->getUri()->getHost(), 'example.org'); 241 } 242} 243``` 244 245```php 246namespace App\RequestMatcher; 247 248use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface; 249use Psr\Http\Message\RequestInterface; 250 251class TwitterRequestMatcher implements RequestMatcherInterface 252{ 253 254 /** 255 * @inheritDoc 256 */ 257 public function matches(RequestInterface $request) 258 { 259 return false !== strpos($request->getUri()->getHost(), 'twitter.com'); 260 } 261} 262``` 263 264```php 265require_once __DIR__ . '/vendor/autoload.php'; 266 267use App\RequestMatcher\ExampleOrgRequestMatcher; 268use App\RequestMatcher\TwitterRequestMatcher; 269use GuzzleHttp\Client; 270use GuzzleHttp\HandlerStack; 271use Kevinrob\GuzzleCache\CacheMiddleware; 272use Kevinrob\GuzzleCache\Strategy; 273 274$strategy = new Strategy\Delegate\DelegatingCacheStrategy($defaultStrategy = new Strategy\NullCacheStrategy()); 275$strategy->registerRequestMatcher(new ExampleOrgRequestMatcher(), new Strategy\PublicCacheStrategy()); 276$strategy->registerRequestMatcher(new TwitterRequestMatcher(), new Strategy\PrivateCacheStrategy()); 277 278$stack = HandlerStack::create(); 279$stack->push(new CacheMiddleware($strategy)); 280$guzzle = new Client(['handler' => $stack]); 281``` 282 283With this example: 284* All requests to `example.org` will be handled by `PublicCacheStrategy` 285* All requests to `twitter.com` will be handled by `PrivateCacheStrategy` 286* All other requests won't be cached.