Back to Moodle

guzzle-cache-middleware

public/lib/guzzlehttp/kevinrob/guzzlecache/README.md

5.2.07.0 KB
Original Source

guzzle-cache-middleware

A HTTP Cache for Guzzle 6+. It's a simple Middleware to be added in the HandlerStack.

Goals

  • RFC 7234 compliance
  • Performance and transparency
  • Assured compatibility with PSR-7

Built-in storage interfaces

Installation

composer require kevinrob/guzzle-cache-middleware

or add it the your composer.json and run composer update kevinrob/guzzle-cache-middleware.

Why?

Performance. It's very common to do some HTTP calls to an API for rendering a page and it takes times to do it.

How?

With a simple Middleware added at the top of the HandlerStack of Guzzle.

php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Kevinrob\GuzzleCache\CacheMiddleware;

// Create default HandlerStack
$stack = HandlerStack::create();

// Add this middleware to the top with `push`
$stack->push(new CacheMiddleware(), 'cache');

// Initialize the client with the handler option
$client = new Client(['handler' => $stack]);

Examples

Laravel cache

You can use a cache with Laravel, e.g. Redis, Memcache etc.:

php
[...]
use Illuminate\Support\Facades\Cache;
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
use Kevinrob\GuzzleCache\Storage\LaravelCacheStorage;

[...]

$stack->push(
  new CacheMiddleware(
    new PrivateCacheStrategy(
      new LaravelCacheStorage(
        Cache::store('redis')
      )
    )
  ),
  'cache'
);

Flysystem

php
[...]
use League\Flysystem\Local\LocalFilesystemAdapter;
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
use Kevinrob\GuzzleCache\Storage\FlysystemStorage;

[...]

$stack->push(
  new CacheMiddleware(
    new PrivateCacheStrategy(
      new FlysystemStorage(
        new LocalFilesystemAdapter('/path/to/cache')
      )
    )
  ),
  'cache'
);

WordPress Object Cache

php
[...]
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
use Kevinrob\GuzzleCache\Storage\WordPressObjectCacheStorage;

[...]

$stack->push(
  new CacheMiddleware(
    new PrivateCacheStrategy(
      new WordPressObjectCacheStorage()
    )
  ),
  'cache'
);

Public and shared

It's possible to add a public shared cache to the stack:

php
[...]
use Cache\Adapter\PHPArray\ArrayCachePool;
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
use Kevinrob\GuzzleCache\Strategy\PublicCacheStrategy;
use Kevinrob\GuzzleCache\Storage\Psr6CacheStorage;
use League\Flysystem\Local\LocalFilesystemAdapter;
use Kevinrob\GuzzleCache\Storage\FlysystemStorage;

[...]
// Private caching
$stack->push(
  new CacheMiddleware(
    new PrivateCacheStrategy(
      new FlysystemStorage(
        new LocalFilesystemAdapter('/tmp/')
      )
    )
  ),
  'private-cache'
);

// Public caching
$stack->push(
  new CacheMiddleware(
    new PublicCacheStrategy(
      new Psr6CacheStorage(
        new ArrayCachePool()
      )
    )
  ),
  'shared-cache'
);

Greedy caching

In some cases servers might send insufficient or no caching headers at all. Using the greedy caching strategy allows defining an expiry TTL on your own while disregarding any possibly present caching headers:

php
[...]
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
use Kevinrob\GuzzleCache\Strategy\GreedyCacheStrategy;
use Kevinrob\GuzzleCache\Storage\FlysystemStorage;
use League\Flysystem\Local\LocalFilesystemAdapter;

[...]
// Greedy caching
$stack->push(
  new CacheMiddleware(
    new GreedyCacheStrategy(
      new FlysystemStorage(
        new LocalFilesystemAdapter('/tmp/')
      ),
      1800, // the TTL in seconds
      new KeyValueHttpHeader(['Authorization']) // Optional - specify the headers that can change the cache key
    )
  ),
  'greedy-cache'
);

Delegate caching

Because your client may call different apps, on different domains, you may need to define which strategy is suitable to your requests.

To solve this, all you have to do is to define a default cache strategy, and override it by implementing your own Request Matchers.

Here's an example:

php
namespace App\RequestMatcher;

use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface;
use Psr\Http\Message\RequestInterface;

class ExampleOrgRequestMatcher implements RequestMatcherInterface
{

    /**
     * @inheritDoc
     */
    public function matches(RequestInterface $request)
    {
        return false !== strpos($request->getUri()->getHost(), 'example.org');
    }
}
php
namespace App\RequestMatcher;

use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface;
use Psr\Http\Message\RequestInterface;

class TwitterRequestMatcher implements RequestMatcherInterface
{

    /**
     * @inheritDoc
     */
    public function matches(RequestInterface $request)
    {
        return false !== strpos($request->getUri()->getHost(), 'twitter.com');
    }
}
php
require_once __DIR__ . '/vendor/autoload.php';

use App\RequestMatcher\ExampleOrgRequestMatcher;
use App\RequestMatcher\TwitterRequestMatcher;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Kevinrob\GuzzleCache\CacheMiddleware;
use Kevinrob\GuzzleCache\Strategy;

$strategy = new Strategy\Delegate\DelegatingCacheStrategy($defaultStrategy = new Strategy\NullCacheStrategy());
$strategy->registerRequestMatcher(new ExampleOrgRequestMatcher(), new Strategy\PublicCacheStrategy());
$strategy->registerRequestMatcher(new TwitterRequestMatcher(), new Strategy\PrivateCacheStrategy());

$stack = HandlerStack::create();
$stack->push(new CacheMiddleware($strategy));
$guzzle = new Client(['handler' => $stack]);

With this example:

  • All requests to example.org will be handled by PublicCacheStrategy
  • All requests to twitter.com will be handled by PrivateCacheStrategy
  • All other requests won't be cached.

Drupal

See Guzzle Cache module.

Links that talk about the project

Buy me a coffee

If you like this project, you can buy me a coffee! (or a beer 😉)

Development

Docker quick start

Initialization

bash
make init

Running test

bash
make test

Entering container shell

bash
make shell