实现请求缓存1天。
This commit is contained in:
22
vendor/kevinrob/guzzle-cache-middleware/LICENSE
vendored
Normal file
22
vendor/kevinrob/guzzle-cache-middleware/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Kevin Robatel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
309
vendor/kevinrob/guzzle-cache-middleware/README.md
vendored
Normal file
309
vendor/kevinrob/guzzle-cache-middleware/README.md
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
# guzzle-cache-middleware
|
||||
|
||||
[](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) [](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) [](https://packagist.org/packages/kevinrob/guzzle-cache-middleware)
|
||||
 [](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/?branch=master) [](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/?branch=master)
|
||||
|
||||
|
||||
A HTTP Cache for [Guzzle](https://github.com/guzzle/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
|
||||
- [Doctrine cache](https://github.com/doctrine/cache)
|
||||
- [Laravel cache](https://laravel.com/docs/5.2/cache)
|
||||
- [Flysystem](https://github.com/thephpleague/flysystem)
|
||||
- [PSR6](https://github.com/php-fig/cache)
|
||||
- [WordPress Object Cache](https://codex.wordpress.org/Class_Reference/WP_Object_Cache)
|
||||
|
||||
## 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
|
||||
|
||||
## Doctrine/Cache
|
||||
You can use a cache from `Doctrine/Cache`:
|
||||
```php
|
||||
[...]
|
||||
use Doctrine\Common\Cache\FilesystemCache;
|
||||
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||
|
||||
[...]
|
||||
$stack->push(
|
||||
new CacheMiddleware(
|
||||
new PrivateCacheStrategy(
|
||||
new DoctrineCacheStorage(
|
||||
new FilesystemCache('/tmp/')
|
||||
)
|
||||
)
|
||||
),
|
||||
'cache'
|
||||
);
|
||||
```
|
||||
|
||||
You can use `ChainCache` for using multiple `CacheProvider` instances. With that provider, you have to sort the different caches from the faster to the slower. Like that, you can have a very fast cache.
|
||||
```php
|
||||
[...]
|
||||
use Doctrine\Common\Cache\ChainCache;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\Common\Cache\FilesystemCache;
|
||||
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||
|
||||
[...]
|
||||
$stack->push(new CacheMiddleware(
|
||||
new PrivateCacheStrategy(
|
||||
new DoctrineCacheStorage(
|
||||
new ChainCache([
|
||||
new ArrayCache(),
|
||||
new FilesystemCache('/tmp/'),
|
||||
])
|
||||
)
|
||||
)
|
||||
), 'cache');
|
||||
```
|
||||
|
||||
## 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\Adapter\Local;
|
||||
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||
use Kevinrob\GuzzleCache\Storage\FlysystemStorage;
|
||||
|
||||
[...]
|
||||
|
||||
$stack->push(
|
||||
new CacheMiddleware(
|
||||
new PrivateCacheStrategy(
|
||||
new FlysystemStorage(
|
||||
new Local('/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 Doctrine\Common\Cache\FilesystemCache;
|
||||
use Doctrine\Common\Cache\PredisCache;
|
||||
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||
use Kevinrob\GuzzleCache\Strategy\PublicCacheStrategy;
|
||||
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||
|
||||
[...]
|
||||
// Private caching
|
||||
$stack->push(
|
||||
new CacheMiddleware(
|
||||
new PrivateCacheStrategy(
|
||||
new DoctrineCacheStorage(
|
||||
new FilesystemCache('/tmp/')
|
||||
)
|
||||
)
|
||||
),
|
||||
'private-cache'
|
||||
);
|
||||
|
||||
// Public caching
|
||||
$stack->push(
|
||||
new CacheMiddleware(
|
||||
new PublicCacheStrategy(
|
||||
new DoctrineCacheStorage(
|
||||
new PredisCache(
|
||||
new Predis\Client('tcp://10.0.0.1:6379')
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
'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\DoctrineCacheStorage;
|
||||
use Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
[...]
|
||||
// Greedy caching
|
||||
$stack->push(
|
||||
new CacheMiddleware(
|
||||
new GreedyCacheStrategy(
|
||||
new DoctrineCacheStorage(
|
||||
new FilesystemCache('/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](https://www.drupal.org/project/guzzle_cache) module.
|
||||
|
||||
# Links that talk about the project
|
||||
- [Speeding Up APIs/Apps/Smart Toasters with HTTP Response Caching](https://apisyouwonthate.com/blog/speeding-up-apis-apps-smart-toasters-with-http-response-caching)
|
||||
- [Caching HTTP-Requests with Guzzle 6 and PSR-6](http://a.kabachnik.info/caching-http-requests-with-guzzle-6-and-psr-6.html)
|
||||
|
||||
# Development
|
||||
|
||||
## Docker quick start
|
||||
|
||||
### Initialization
|
||||
```bash
|
||||
make init
|
||||
```
|
||||
### Running test
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
### Entering container shell
|
||||
```bash
|
||||
make shell
|
||||
```
|
||||
58
vendor/kevinrob/guzzle-cache-middleware/composer.json
vendored
Normal file
58
vendor/kevinrob/guzzle-cache-middleware/composer.json
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "kevinrob/guzzle-cache-middleware",
|
||||
"type": "library",
|
||||
"description": "A HTTP/1.1 Cache for Guzzle 6. It's a simple Middleware to be added in the HandlerStack. (RFC 7234)",
|
||||
"keywords": ["guzzle", "guzzle6", "cache", "http", "http 1.1", "psr6", "psr7", "handler", "middleware", "cache-control", "rfc7234", "performance", "php", "promise", "expiration", "validation", "Etag", "flysystem", "doctrine"],
|
||||
"homepage": "https://github.com/Kevinrob/guzzle-cache-middleware",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kevin Robatel",
|
||||
"email": "kevinrob2@gmail.com",
|
||||
"homepage": "https://github.com/Kevinrob"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2.0",
|
||||
"guzzlehttp/guzzle": "^6.0 || ^7.0",
|
||||
"guzzlehttp/promises": "^1.4 || ^2.0",
|
||||
"guzzlehttp/psr7": "^1.7.0 || ^2.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5.15 || ^9.5",
|
||||
"doctrine/cache": "^1.10",
|
||||
"league/flysystem": "^2.5",
|
||||
"psr/cache": "^1.0",
|
||||
"cache/array-adapter": "^0.4 || ^0.5 || ^1.0",
|
||||
"illuminate/cache": "^5.0",
|
||||
"cache/simple-cache-bridge": "^0.1 || ^1.0",
|
||||
"symfony/phpunit-bridge": "^4.4 || ^5.0",
|
||||
"symfony/cache": "^4.4 || ^5.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Kevinrob\\GuzzleCache\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Kevinrob\\GuzzleCache\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"guzzlehttp/guzzle": "For using this library. It was created for Guzzle6 (but you can use it with any PSR-7 HTTP client).",
|
||||
"doctrine/cache": "This library has a lot of ready-to-use cache storage (to be used with Kevinrob\\GuzzleCache\\Storage\\DoctrineCacheStorage). Use only versions >=1.4.0 < 2.0.0",
|
||||
"league/flysystem": "To be used with Kevinrob\\GuzzleCache\\Storage\\FlysystemStorage",
|
||||
"psr/cache": "To be used with Kevinrob\\GuzzleCache\\Storage\\Psr6CacheStorage",
|
||||
"psr/simple-cache": "To be used with Kevinrob\\GuzzleCache\\Storage\\Psr16CacheStorage",
|
||||
"laravel/framework": "To be used with Kevinrob\\GuzzleCache\\Storage\\LaravelCacheStorage"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"kylekatarnls/update-helper": true
|
||||
}
|
||||
}
|
||||
}
|
||||
46
vendor/kevinrob/guzzle-cache-middleware/src/BodyStore.php
vendored
Normal file
46
vendor/kevinrob/guzzle-cache-middleware/src/BodyStore.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache;
|
||||
|
||||
/**
|
||||
*
|
||||
* This object is only meant to provide a callable to `GuzzleHttp\Psr7\PumpStream`.
|
||||
*
|
||||
* @internal don't use it in your project.
|
||||
*/
|
||||
class BodyStore
|
||||
{
|
||||
private $body;
|
||||
|
||||
private $read = 0;
|
||||
|
||||
private $toRead;
|
||||
|
||||
public function __construct(string $body)
|
||||
{
|
||||
$this->body = $body;
|
||||
$this->toRead = mb_strlen($this->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
* @return false|string
|
||||
*/
|
||||
public function __invoke(int $length)
|
||||
{
|
||||
if ($this->toRead <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$length = min($length, $this->toRead);
|
||||
|
||||
$body = mb_substr(
|
||||
$this->body,
|
||||
$this->read,
|
||||
$length
|
||||
);
|
||||
$this->toRead -= $length;
|
||||
$this->read += $length;
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
333
vendor/kevinrob/guzzle-cache-middleware/src/CacheEntry.php
vendored
Normal file
333
vendor/kevinrob/guzzle-cache-middleware/src/CacheEntry.php
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache;
|
||||
|
||||
use GuzzleHttp\Psr7\PumpStream;
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class CacheEntry implements \Serializable
|
||||
{
|
||||
/**
|
||||
* @var RequestInterface
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var ResponseInterface
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $staleAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $staleIfErrorTo;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $staleWhileRevalidateTo;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $dateCreated;
|
||||
|
||||
/**
|
||||
* Cached timestamp of staleAt variable.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $timestampStale;
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param \DateTime $staleAt
|
||||
* @param \DateTime|null $staleIfErrorTo if null, detected with the headers (RFC 5861)
|
||||
* @param \DateTime|null $staleWhileRevalidateTo
|
||||
*/
|
||||
public function __construct(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
\DateTime $staleAt,
|
||||
\DateTime $staleIfErrorTo = null,
|
||||
\DateTime $staleWhileRevalidateTo = null
|
||||
) {
|
||||
$this->dateCreated = new \DateTime();
|
||||
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->staleAt = $staleAt;
|
||||
|
||||
$values = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
|
||||
|
||||
if ($staleIfErrorTo === null && $values->has('stale-if-error')) {
|
||||
$this->staleIfErrorTo = (new \DateTime(
|
||||
'@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-if-error'))
|
||||
));
|
||||
} else {
|
||||
$this->staleIfErrorTo = $staleIfErrorTo;
|
||||
}
|
||||
|
||||
if ($staleWhileRevalidateTo === null && $values->has('stale-while-revalidate')) {
|
||||
$this->staleWhileRevalidateTo = new \DateTime(
|
||||
'@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-while-revalidate'))
|
||||
);
|
||||
} else {
|
||||
$this->staleWhileRevalidateTo = $staleWhileRevalidateTo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response
|
||||
->withHeader('Age', $this->getAge());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function getOriginalResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function getOriginalRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @return bool
|
||||
*/
|
||||
public function isVaryEquals(RequestInterface $request)
|
||||
{
|
||||
if ($this->response->hasHeader('Vary')) {
|
||||
if ($this->request === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->getVaryHeaders() as $key => $value) {
|
||||
if (!$this->request->hasHeader($key)
|
||||
&& !$request->hasHeader($key)
|
||||
) {
|
||||
// Absent from both
|
||||
continue;
|
||||
} elseif ($this->request->getHeaderLine($key)
|
||||
== $request->getHeaderLine($key)
|
||||
) {
|
||||
// Same content
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vary headers that should be honoured by the cache.
|
||||
*
|
||||
* @return KeyValueHttpHeader
|
||||
*/
|
||||
public function getVaryHeaders()
|
||||
{
|
||||
return new KeyValueHttpHeader($this->response->getHeader('Vary'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getStaleAt()
|
||||
{
|
||||
return $this->staleAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isFresh()
|
||||
{
|
||||
return !$this->isStale();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStale()
|
||||
{
|
||||
return $this->getStaleAge() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int positive value equal staled
|
||||
*/
|
||||
public function getStaleAge()
|
||||
{
|
||||
// This object is immutable
|
||||
if ($this->timestampStale === null) {
|
||||
$this->timestampStale = $this->staleAt->getTimestamp();
|
||||
}
|
||||
|
||||
return time() - $this->timestampStale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function serveStaleIfError()
|
||||
{
|
||||
return $this->staleIfErrorTo !== null
|
||||
&& $this->staleIfErrorTo->getTimestamp() >= (new \DateTime())->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function staleWhileValidate()
|
||||
{
|
||||
return $this->staleWhileRevalidateTo !== null
|
||||
&& $this->staleWhileRevalidateTo->getTimestamp() >= (new \DateTime())->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasValidationInformation()
|
||||
{
|
||||
return $this->response->hasHeader('Etag') || $this->response->hasHeader('Last-Modified');
|
||||
}
|
||||
|
||||
/**
|
||||
* Time in seconds how long the entry should be kept in the cache
|
||||
*
|
||||
* This will not give the time (in seconds) that the response will still be fresh for
|
||||
* from the HTTP point of view, but an upper bound on how long it is necessary and
|
||||
* reasonable to keep the response in a cache (to re-use it or re-validate it later on).
|
||||
*
|
||||
* @return int TTL in seconds (0 = infinite)
|
||||
*/
|
||||
public function getTTL()
|
||||
{
|
||||
if ($this->hasValidationInformation()) {
|
||||
// No TTL if we have a way to re-validate the cache
|
||||
return 0;
|
||||
}
|
||||
|
||||
$ttl = 0;
|
||||
|
||||
// Keep it when stale if error
|
||||
if ($this->staleIfErrorTo !== null) {
|
||||
$ttl = max($ttl, $this->staleIfErrorTo->getTimestamp() - time());
|
||||
}
|
||||
|
||||
// Keep it when stale-while-revalidate
|
||||
if ($this->staleWhileRevalidateTo !== null) {
|
||||
$ttl = max($ttl, $this->staleWhileRevalidateTo->getTimestamp() - time());
|
||||
}
|
||||
|
||||
// Keep it until it become stale
|
||||
$ttl = max($ttl, $this->staleAt->getTimestamp() - time());
|
||||
|
||||
// Don't return 0, it's reserved for infinite TTL
|
||||
return $ttl !== 0 ? (int) $ttl : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Age in seconds
|
||||
*/
|
||||
public function getAge()
|
||||
{
|
||||
return time() - $this->dateCreated->getTimestamp();
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return [
|
||||
'request' => self::toSerializeableMessage($this->request),
|
||||
'response' => $this->response !== null ? self::toSerializeableMessage($this->response) : null,
|
||||
'staleAt' => $this->staleAt,
|
||||
'staleIfErrorTo' => $this->staleIfErrorTo,
|
||||
'staleWhileRevalidateTo' => $this->staleWhileRevalidateTo,
|
||||
'dateCreated' => $this->dateCreated,
|
||||
'timestampStale' => $this->timestampStale,
|
||||
];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
$prefix = '';
|
||||
if (isset($data["\0*\0request"])) {
|
||||
// We are unserializing a cache entry which was serialized with a version < 4.1.1
|
||||
$prefix = "\0*\0";
|
||||
}
|
||||
$this->request = self::restoreStreamBody($data[$prefix.'request']);
|
||||
$this->response = $data[$prefix.'response'] !== null ? self::restoreStreamBody($data[$prefix.'response']) : null;
|
||||
$this->staleAt = $data[$prefix.'staleAt'];
|
||||
$this->staleIfErrorTo = $data[$prefix.'staleIfErrorTo'];
|
||||
$this->staleWhileRevalidateTo = $data[$prefix.'staleWhileRevalidateTo'];
|
||||
$this->dateCreated = $data[$prefix.'dateCreated'];
|
||||
$this->timestampStale = $data[$prefix.'timestampStale'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream/Resource can't be serialized... So we copy the content into an implementation of `Psr\Http\Message\StreamInterface`
|
||||
*
|
||||
* @template T of MessageInterface
|
||||
*
|
||||
* @param T $message
|
||||
* @return T
|
||||
*/
|
||||
private static function toSerializeableMessage(MessageInterface $message): MessageInterface
|
||||
{
|
||||
$bodyString = (string)$message->getBody();
|
||||
|
||||
return $message->withBody(
|
||||
new PumpStream(
|
||||
new BodyStore($bodyString),
|
||||
[
|
||||
'size' => mb_strlen($bodyString),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of MessageInterface
|
||||
*
|
||||
* @param T $message
|
||||
* @return T
|
||||
*/
|
||||
private static function restoreStreamBody(MessageInterface $message): MessageInterface
|
||||
{
|
||||
return $message->withBody(
|
||||
\GuzzleHttp\Psr7\Utils::streamFor((string) $message->getBody())
|
||||
);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->__serialize());
|
||||
}
|
||||
|
||||
public function unserialize($data)
|
||||
{
|
||||
$this->__unserialize(unserialize($data));
|
||||
}
|
||||
}
|
||||
400
vendor/kevinrob/guzzle-cache-middleware/src/CacheMiddleware.php
vendored
Normal file
400
vendor/kevinrob/guzzle-cache-middleware/src/CacheMiddleware.php
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use GuzzleHttp\Promise\FulfilledPromise;
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Kevinrob\GuzzleCache\Strategy\CacheStrategyInterface;
|
||||
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class CacheMiddleware.
|
||||
*/
|
||||
class CacheMiddleware
|
||||
{
|
||||
const HEADER_RE_VALIDATION = 'X-Kevinrob-GuzzleCache-ReValidation';
|
||||
const HEADER_INVALIDATION = 'X-Kevinrob-GuzzleCache-Invalidation';
|
||||
const HEADER_CACHE_INFO = 'X-Kevinrob-Cache';
|
||||
const HEADER_CACHE_HIT = 'HIT';
|
||||
const HEADER_CACHE_MISS = 'MISS';
|
||||
const HEADER_CACHE_STALE = 'STALE';
|
||||
|
||||
/**
|
||||
* @var array of Promise
|
||||
*/
|
||||
protected $waitingRevalidate = [];
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var CacheStrategyInterface
|
||||
*/
|
||||
protected $cacheStorage;
|
||||
|
||||
/**
|
||||
* List of allowed HTTP methods to cache
|
||||
* Key = method name (upscaling)
|
||||
* Value = true.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $httpMethods = ['GET' => true];
|
||||
|
||||
/**
|
||||
* List of safe methods
|
||||
*
|
||||
* https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $safeMethods = ['GET' => true, 'HEAD' => true, 'OPTIONS' => true, 'TRACE' => true];
|
||||
|
||||
/**
|
||||
* @param CacheStrategyInterface|null $cacheStrategy
|
||||
*/
|
||||
public function __construct(CacheStrategyInterface $cacheStrategy = null)
|
||||
{
|
||||
$this->cacheStorage = $cacheStrategy !== null ? $cacheStrategy : new PrivateCacheStrategy();
|
||||
|
||||
register_shutdown_function([$this, 'purgeReValidation']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
*/
|
||||
public function setClient(Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CacheStrategyInterface $cacheStorage
|
||||
*/
|
||||
public function setCacheStorage(CacheStrategyInterface $cacheStorage)
|
||||
{
|
||||
$this->cacheStorage = $cacheStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CacheStrategyInterface
|
||||
*/
|
||||
public function getCacheStorage()
|
||||
{
|
||||
return $this->cacheStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $methods
|
||||
*/
|
||||
public function setHttpMethods(array $methods)
|
||||
{
|
||||
$this->httpMethods = $methods;
|
||||
}
|
||||
|
||||
public function getHttpMethods()
|
||||
{
|
||||
return $this->httpMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called at the end of the script.
|
||||
*/
|
||||
public function purgeReValidation()
|
||||
{
|
||||
\GuzzleHttp\Promise\Utils::inspectAll($this->waitingRevalidate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $handler
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function __invoke(callable $handler)
|
||||
{
|
||||
return function (RequestInterface $request, array $options) use (&$handler) {
|
||||
if (!isset($this->httpMethods[strtoupper($request->getMethod())])) {
|
||||
// No caching for this method allowed
|
||||
|
||||
return $handler($request, $options)->then(
|
||||
function (ResponseInterface $response) use ($request) {
|
||||
if (!isset($this->safeMethods[$request->getMethod()])) {
|
||||
// Invalidate cache after a call of non-safe method on the same URI
|
||||
$response = $this->invalidateCache($request, $response);
|
||||
}
|
||||
|
||||
return $response->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_MISS);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($request->hasHeader(self::HEADER_RE_VALIDATION)) {
|
||||
// It's a re-validation request, so bypass the cache!
|
||||
return $handler($request->withoutHeader(self::HEADER_RE_VALIDATION), $options);
|
||||
}
|
||||
|
||||
// Retrieve information from request (Cache-Control)
|
||||
$reqCacheControl = new KeyValueHttpHeader($request->getHeader('Cache-Control'));
|
||||
$onlyFromCache = $reqCacheControl->has('only-if-cached');
|
||||
$staleResponse = $reqCacheControl->has('max-stale')
|
||||
&& $reqCacheControl->get('max-stale') === '';
|
||||
$maxStaleCache = $reqCacheControl->get('max-stale', null);
|
||||
$minFreshCache = $reqCacheControl->get('min-fresh', null);
|
||||
|
||||
// If cache => return new FulfilledPromise(...) with response
|
||||
$cacheEntry = $this->cacheStorage->fetch($request);
|
||||
if ($cacheEntry instanceof CacheEntry) {
|
||||
$body = $cacheEntry->getResponse()->getBody();
|
||||
if ($body->tell() > 0) {
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
if ($cacheEntry->isFresh()
|
||||
&& ($minFreshCache === null || $cacheEntry->getStaleAge() + (int)$minFreshCache <= 0)
|
||||
) {
|
||||
// Cache HIT!
|
||||
return new FulfilledPromise(
|
||||
$cacheEntry->getResponse()->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_HIT)
|
||||
);
|
||||
} elseif ($staleResponse
|
||||
|| ($maxStaleCache !== null && $cacheEntry->getStaleAge() <= $maxStaleCache)
|
||||
) {
|
||||
// Staled cache!
|
||||
return new FulfilledPromise(
|
||||
$cacheEntry->getResponse()->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_HIT)
|
||||
);
|
||||
} elseif ($cacheEntry->hasValidationInformation() && !$onlyFromCache) {
|
||||
// Re-validation header
|
||||
$request = static::getRequestWithReValidationHeader($request, $cacheEntry);
|
||||
|
||||
if ($cacheEntry->staleWhileValidate()) {
|
||||
static::addReValidationRequest($request, $this->cacheStorage, $cacheEntry);
|
||||
|
||||
return new FulfilledPromise(
|
||||
$cacheEntry->getResponse()
|
||||
->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_STALE)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$cacheEntry = null;
|
||||
}
|
||||
|
||||
if ($cacheEntry === null && $onlyFromCache) {
|
||||
// Explicit asking of a cached response => 504
|
||||
return new FulfilledPromise(
|
||||
new Response(504)
|
||||
);
|
||||
}
|
||||
|
||||
/** @var Promise $promise */
|
||||
$promise = $handler($request, $options);
|
||||
|
||||
return $promise->then(
|
||||
function (ResponseInterface $response) use ($request, $cacheEntry) {
|
||||
// Check if error and looking for a staled content
|
||||
if ($response->getStatusCode() >= 500) {
|
||||
$responseStale = static::getStaleResponse($cacheEntry);
|
||||
if ($responseStale instanceof ResponseInterface) {
|
||||
return $responseStale;
|
||||
}
|
||||
}
|
||||
|
||||
$update = false;
|
||||
|
||||
if ($response->getStatusCode() == 304 && $cacheEntry instanceof CacheEntry) {
|
||||
// Not modified => cache entry is re-validate
|
||||
/** @var ResponseInterface $response */
|
||||
$response = $response
|
||||
->withStatus($cacheEntry->getResponse()->getStatusCode())
|
||||
->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_HIT);
|
||||
$response = $response->withBody($cacheEntry->getResponse()->getBody());
|
||||
|
||||
// Merge headers of the "304 Not Modified" and the cache entry
|
||||
/**
|
||||
* @var string $headerName
|
||||
* @var string[] $headerValue
|
||||
*/
|
||||
foreach ($cacheEntry->getOriginalResponse()->getHeaders() as $headerName => $headerValue) {
|
||||
if (!$response->hasHeader($headerName) && $headerName !== self::HEADER_CACHE_INFO) {
|
||||
$response = $response->withHeader($headerName, $headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
$update = true;
|
||||
} else {
|
||||
$response = $response->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_MISS);
|
||||
}
|
||||
|
||||
return static::addToCache($this->cacheStorage, $request, $response, $update);
|
||||
},
|
||||
function ($reason) use ($cacheEntry) {
|
||||
$response = static::getStaleResponse($cacheEntry);
|
||||
if ($response instanceof ResponseInterface) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return new RejectedPromise($reason);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CacheStrategyInterface $cache
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param bool $update cache
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
protected static function addToCache(
|
||||
CacheStrategyInterface $cache,
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
$update = false
|
||||
) {
|
||||
$body = $response->getBody();
|
||||
|
||||
// If the body is not seekable, we have to replace it by a seekable one
|
||||
if (!$body->isSeekable()) {
|
||||
$response = $response->withBody(
|
||||
\GuzzleHttp\Psr7\Utils::streamFor($body->getContents())
|
||||
);
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
$cache->update($request, $response);
|
||||
} else {
|
||||
$cache->cache($request, $response);
|
||||
}
|
||||
|
||||
// always rewind back to the start otherwise other middlewares may get empty "content"
|
||||
if ($body->isSeekable()) {
|
||||
$response->getBody()->rewind();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param CacheStrategyInterface $cacheStorage
|
||||
* @param CacheEntry $cacheEntry
|
||||
*
|
||||
* @return bool if added
|
||||
*/
|
||||
protected function addReValidationRequest(
|
||||
RequestInterface $request,
|
||||
CacheStrategyInterface &$cacheStorage,
|
||||
CacheEntry $cacheEntry
|
||||
) {
|
||||
// Add the promise for revalidate
|
||||
if ($this->client !== null) {
|
||||
/** @var RequestInterface $request */
|
||||
$request = $request->withHeader(self::HEADER_RE_VALIDATION, '1');
|
||||
$this->waitingRevalidate[] = $this->client
|
||||
->sendAsync($request)
|
||||
->then(function (ResponseInterface $response) use ($request, &$cacheStorage, $cacheEntry) {
|
||||
$update = false;
|
||||
|
||||
if ($response->getStatusCode() == 304) {
|
||||
// Not modified => cache entry is re-validate
|
||||
/** @var ResponseInterface $response */
|
||||
$response = $response->withStatus($cacheEntry->getResponse()->getStatusCode());
|
||||
$response = $response->withBody($cacheEntry->getResponse()->getBody());
|
||||
|
||||
// Merge headers of the "304 Not Modified" and the cache entry
|
||||
foreach ($cacheEntry->getResponse()->getHeaders() as $headerName => $headerValue) {
|
||||
if (!$response->hasHeader($headerName)) {
|
||||
$response = $response->withHeader($headerName, $headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
$update = true;
|
||||
}
|
||||
|
||||
static::addToCache($cacheStorage, $request, $response, $update);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CacheEntry|null $cacheEntry
|
||||
*
|
||||
* @return null|ResponseInterface
|
||||
*/
|
||||
protected static function getStaleResponse(CacheEntry $cacheEntry = null)
|
||||
{
|
||||
// Return staled cache entry if we can
|
||||
if ($cacheEntry instanceof CacheEntry && $cacheEntry->serveStaleIfError()) {
|
||||
return $cacheEntry->getResponse()
|
||||
->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_STALE);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param CacheEntry $cacheEntry
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
protected static function getRequestWithReValidationHeader(RequestInterface $request, CacheEntry $cacheEntry)
|
||||
{
|
||||
if ($cacheEntry->getResponse()->hasHeader('Last-Modified')) {
|
||||
$request = $request->withHeader(
|
||||
'If-Modified-Since',
|
||||
$cacheEntry->getResponse()->getHeader('Last-Modified')
|
||||
);
|
||||
}
|
||||
if ($cacheEntry->getResponse()->hasHeader('Etag')) {
|
||||
$request = $request->withHeader(
|
||||
'If-None-Match',
|
||||
$cacheEntry->getResponse()->getHeader('Etag')
|
||||
);
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CacheStrategyInterface|null $cacheStorage
|
||||
*
|
||||
* @return CacheMiddleware the Middleware for Guzzle HandlerStack
|
||||
*
|
||||
* @deprecated Use constructor => `new CacheMiddleware()`
|
||||
*/
|
||||
public static function getMiddleware(CacheStrategyInterface $cacheStorage = null)
|
||||
{
|
||||
return new self($cacheStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
private function invalidateCache(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
foreach (array_keys($this->httpMethods) as $method) {
|
||||
$this->cacheStorage->delete($request->withMethod($method));
|
||||
}
|
||||
|
||||
return $response->withHeader(self::HEADER_INVALIDATION, true);
|
||||
}
|
||||
}
|
||||
131
vendor/kevinrob/guzzle-cache-middleware/src/KeyValueHttpHeader.php
vendored
Normal file
131
vendor/kevinrob/guzzle-cache-middleware/src/KeyValueHttpHeader.php
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache;
|
||||
|
||||
class KeyValueHttpHeader implements \Iterator
|
||||
{
|
||||
/**
|
||||
* Take from https://github.com/hapijs/wreck.
|
||||
*/
|
||||
const REGEX_SPLIT = '/(?:^|(?:\s*\,\s*))([^\x00-\x20\(\)<>@\,;\:\\\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\\\]|\\\\.)*)\")))?/';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $values = [];
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
foreach ($values as $value) {
|
||||
$matches = [];
|
||||
if (preg_match_all(self::REGEX_SPLIT, $value, $matches, PREG_SET_ORDER)) {
|
||||
foreach ($matches as $match) {
|
||||
$val = '';
|
||||
if (count($match) == 3) {
|
||||
$val = $match[2];
|
||||
} elseif (count($match) > 3) {
|
||||
$val = $match[3];
|
||||
}
|
||||
|
||||
$this->values[$match[1]] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
// For performance, we can use isset,
|
||||
// but it will not match if value == 0
|
||||
return isset($this->values[$key]) || array_key_exists($key, $this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $default the value to return if don't exist
|
||||
* @return string
|
||||
*/
|
||||
public function get($key, $default = '')
|
||||
{
|
||||
if ($this->has($key)) {
|
||||
return $this->values[$key];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return count($this->values) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
* @return mixed Can return any type.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return current($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
* @return void Any returned value is ignored.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
next($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
* @since 5.0.0
|
||||
*
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return key($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link http://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return key($this->values) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
reset($this->values);
|
||||
}
|
||||
}
|
||||
30
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CacheStorageInterface.php
vendored
Normal file
30
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CacheStorageInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
interface CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return CacheEntry|null the data or false
|
||||
*/
|
||||
public function fetch($key);
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param CacheEntry $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save($key, CacheEntry $data);
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key);
|
||||
}
|
||||
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CompressedDoctrineCacheStorage.php
vendored
Normal file
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CompressedDoctrineCacheStorage.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
class CompressedDoctrineCacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @param Cache $cache
|
||||
*/
|
||||
public function __construct(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
try {
|
||||
$cache = unserialize(gzuncompress($this->cache->fetch($key)));
|
||||
if ($cache instanceof CacheEntry) {
|
||||
return $cache;
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
try {
|
||||
$lifeTime = $data->getTTL();
|
||||
if ($lifeTime >= 0) {
|
||||
return $this->cache->save(
|
||||
$key,
|
||||
gzcompress(serialize($data)),
|
||||
$lifeTime
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
// No fail if we can't save it the storage
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
try {
|
||||
return $this->cache->delete($key);
|
||||
} catch (\Exception $ignored) {
|
||||
// Don't fail if we can't delete it
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/DoctrineCacheStorage.php
vendored
Normal file
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/DoctrineCacheStorage.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
class DoctrineCacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @param Cache $cache
|
||||
*/
|
||||
public function __construct(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
try {
|
||||
$cache = unserialize($this->cache->fetch($key));
|
||||
if ($cache instanceof CacheEntry) {
|
||||
return $cache;
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
try {
|
||||
$lifeTime = $data->getTTL();
|
||||
if ($lifeTime >= 0) {
|
||||
return $this->cache->save(
|
||||
$key,
|
||||
serialize($data),
|
||||
$lifeTime
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
// No fail if we can't save it the storage
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
try {
|
||||
return $this->cache->delete($key);
|
||||
} catch (\Exception $ignored) {
|
||||
// Don't fail if we can't delete it
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
61
vendor/kevinrob/guzzle-cache-middleware/src/Storage/FlysystemStorage.php
vendored
Normal file
61
vendor/kevinrob/guzzle-cache-middleware/src/Storage/FlysystemStorage.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
use League\Flysystem\Filesystem;
|
||||
use League\Flysystem\FilesystemAdapter;
|
||||
use League\Flysystem\FilesystemException;
|
||||
|
||||
class FlysystemStorage implements CacheStorageInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Filesystem
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
public function __construct(FilesystemAdapter $adapter)
|
||||
{
|
||||
$this->filesystem = new Filesystem($adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
if ($this->filesystem->fileExists($key)) {
|
||||
// The file exist, read it!
|
||||
$data = @unserialize(
|
||||
$this->filesystem->read($key)
|
||||
);
|
||||
|
||||
if ($data instanceof CacheEntry) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
$this->filesystem->write($key, serialize($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
try {
|
||||
$this->filesystem->delete($key);
|
||||
} catch (FilesystemException $ex) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
vendor/kevinrob/guzzle-cache-middleware/src/Storage/LaravelCacheStorage.php
vendored
Normal file
84
vendor/kevinrob/guzzle-cache-middleware/src/Storage/LaravelCacheStorage.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
class LaravelCacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @param Cache $cache
|
||||
*/
|
||||
public function __construct(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
try {
|
||||
$cache = unserialize($this->cache->get($key, ''));
|
||||
if ($cache instanceof CacheEntry) {
|
||||
return $cache;
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
try {
|
||||
$lifeTime = $this->getLifeTime($data);
|
||||
if ($lifeTime === 0) {
|
||||
return $this->cache->forever(
|
||||
$key,
|
||||
serialize($data)
|
||||
);
|
||||
} else if ($lifeTime > 0) {
|
||||
return $this->cache->add(
|
||||
$key,
|
||||
serialize($data),
|
||||
$lifeTime
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
// No fail if we can't save it the storage
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->cache->forget($key);
|
||||
}
|
||||
|
||||
protected function getLifeTime(CacheEntry $data)
|
||||
{
|
||||
$version = app()->version();
|
||||
if (preg_match('/^\d+(\.\d+)?(\.\d+)?/', $version) && version_compare($version, '5.8.0') < 0) {
|
||||
// getTTL returns seconds, Laravel needs minutes before v5.8
|
||||
return $data->getTTL() / 60;
|
||||
}
|
||||
|
||||
return $data->getTTL();
|
||||
}
|
||||
}
|
||||
52
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr16CacheStorage.php
vendored
Normal file
52
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr16CacheStorage.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
class Psr16CacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* @var CacheInterface
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
public function __construct(CacheInterface $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
$data = $this->cache->get($key);
|
||||
if ($data instanceof CacheEntry) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
$ttl = $data->getTTL();
|
||||
if ($ttl === 0) {
|
||||
return $this->cache->set($key, $data);
|
||||
}
|
||||
return $this->cache->set($key, $data, $data->getTTL());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->cache->delete($key);
|
||||
}
|
||||
}
|
||||
92
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr6CacheStorage.php
vendored
Normal file
92
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr6CacheStorage.php
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
class Psr6CacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* The cache pool.
|
||||
*
|
||||
* @var CacheItemPoolInterface
|
||||
*/
|
||||
protected $cachePool;
|
||||
|
||||
/**
|
||||
* The last item retrieved from the cache.
|
||||
*
|
||||
* This item is transiently stored so that save() can reuse the cache item
|
||||
* usually retrieved by fetch() beforehand, instead of requesting it a second time.
|
||||
*
|
||||
* @var CacheItemInterface|null
|
||||
*/
|
||||
protected $lastItem;
|
||||
|
||||
/**
|
||||
* @param CacheItemPoolInterface $cachePool
|
||||
*/
|
||||
public function __construct(CacheItemPoolInterface $cachePool)
|
||||
{
|
||||
$this->cachePool = $cachePool;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
$item = $this->cachePool->getItem($key);
|
||||
$this->lastItem = $item;
|
||||
|
||||
$cache = $item->get();
|
||||
|
||||
if ($cache instanceof CacheEntry) {
|
||||
return $cache;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
if ($this->lastItem && $this->lastItem->getKey() == $key) {
|
||||
$item = $this->lastItem;
|
||||
} else {
|
||||
$item = $this->cachePool->getItem($key);
|
||||
}
|
||||
|
||||
$this->lastItem = null;
|
||||
|
||||
$item->set($data);
|
||||
|
||||
$ttl = $data->getTTL();
|
||||
if ($ttl === 0) {
|
||||
// No expiration
|
||||
$item->expiresAfter(null);
|
||||
} else {
|
||||
$item->expiresAfter($ttl);
|
||||
}
|
||||
|
||||
return $this->cachePool->save($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
if (null !== $this->lastItem && $this->lastItem->getKey() === $key) {
|
||||
$this->lastItem = null;
|
||||
}
|
||||
|
||||
return $this->cachePool->deleteItem($key);
|
||||
}
|
||||
}
|
||||
60
vendor/kevinrob/guzzle-cache-middleware/src/Storage/VolatileRuntimeStorage.php
vendored
Normal file
60
vendor/kevinrob/guzzle-cache-middleware/src/Storage/VolatileRuntimeStorage.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
/**
|
||||
* This cache class is backed by a PHP Array.
|
||||
*/
|
||||
class VolatileRuntimeStorage implements CacheStorageInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var CacheEntry[]
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return CacheEntry|null the data or false
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
if (isset($this->cache[$key])) {
|
||||
return $this->cache[$key];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param CacheEntry $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
$this->cache[$key] = $data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
if (true === array_key_exists($key, $this->cache)) {
|
||||
unset($this->cache[$key]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
73
vendor/kevinrob/guzzle-cache-middleware/src/Storage/WordPressObjectCacheStorage.php
vendored
Normal file
73
vendor/kevinrob/guzzle-cache-middleware/src/Storage/WordPressObjectCacheStorage.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Storage;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
|
||||
class WordPressObjectCacheStorage implements CacheStorageInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $group;
|
||||
|
||||
/**
|
||||
* @param string $group
|
||||
*/
|
||||
public function __construct($group = 'guzzle')
|
||||
{
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return CacheEntry|null the data or false
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
try {
|
||||
$cache = unserialize(wp_cache_get($key, $this->group));
|
||||
if ($cache instanceof CacheEntry) {
|
||||
return $cache;
|
||||
}
|
||||
} catch (\Exception $ignored) {
|
||||
// Don't fail if we can't load it
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param CacheEntry $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save($key, CacheEntry $data)
|
||||
{
|
||||
try {
|
||||
return wp_cache_set($key, serialize($data), $this->group, $data->getTTL());
|
||||
} catch (\Exception $ignored) {
|
||||
// Don't fail if we can't save it
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
try {
|
||||
return wp_cache_delete($key, $this->group);
|
||||
} catch (\Exception $ignored) {
|
||||
// Don't fail if we can't delete it
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/CacheStrategyInterface.php
vendored
Normal file
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/CacheStrategyInterface.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
interface CacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Return a CacheEntry or null if no cache.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return CacheEntry|null
|
||||
*/
|
||||
public function fetch(RequestInterface $request);
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return bool true if success
|
||||
*/
|
||||
public function cache(RequestInterface $request, ResponseInterface $response);
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return bool true if success
|
||||
*/
|
||||
public function update(RequestInterface $request, ResponseInterface $response);
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(RequestInterface $request);
|
||||
}
|
||||
101
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/DelegatingCacheStrategy.php
vendored
Normal file
101
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/DelegatingCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy\Delegate;
|
||||
|
||||
use Kevinrob\GuzzleCache\Strategy\CacheStrategyInterface;
|
||||
use Kevinrob\GuzzleCache\Strategy\NullCacheStrategy;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class DelegatingCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $requestMatchers = [];
|
||||
|
||||
/**
|
||||
* @var CacheStrategyInterface
|
||||
*/
|
||||
private $defaultCacheStrategy;
|
||||
|
||||
/**
|
||||
* DelegatingCacheStrategy constructor.
|
||||
*/
|
||||
public function __construct(CacheStrategyInterface $defaultCacheStrategy = null)
|
||||
{
|
||||
$this->defaultCacheStrategy = $defaultCacheStrategy ?: new NullCacheStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CacheStrategyInterface $defaultCacheStrategy
|
||||
*/
|
||||
public function setDefaultCacheStrategy(CacheStrategyInterface $defaultCacheStrategy)
|
||||
{
|
||||
$this->defaultCacheStrategy = $defaultCacheStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestMatcherInterface $requestMatcher
|
||||
* @param CacheStrategyInterface $cacheStrategy
|
||||
*/
|
||||
final public function registerRequestMatcher(RequestMatcherInterface $requestMatcher, CacheStrategyInterface $cacheStrategy)
|
||||
{
|
||||
$this->requestMatchers[] = [
|
||||
$requestMatcher,
|
||||
$cacheStrategy,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @return CacheStrategyInterface
|
||||
*/
|
||||
private function getStrategyFor(RequestInterface $request)
|
||||
{
|
||||
/**
|
||||
* @var RequestMatcherInterface $requestMatcher
|
||||
* @var CacheStrategyInterface $cacheStrategy
|
||||
*/
|
||||
foreach ($this->requestMatchers as $requestMatcher) {
|
||||
list($requestMatcher, $cacheStrategy) = $requestMatcher;
|
||||
if ($requestMatcher->matches($request)) {
|
||||
return $cacheStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->defaultCacheStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function fetch(RequestInterface $request)
|
||||
{
|
||||
return $this->getStrategyFor($request)->fetch($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
return $this->getStrategyFor($request)->cache($request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function update(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
return $this->getStrategyFor($request)->update($request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(RequestInterface $request)
|
||||
{
|
||||
return $this->getStrategyFor($request)->delete($request);
|
||||
}
|
||||
}
|
||||
15
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/RequestMatcherInterface.php
vendored
Normal file
15
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/RequestMatcherInterface.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy\Delegate;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
interface RequestMatcherInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @return bool
|
||||
*/
|
||||
public function matches(RequestInterface $request);
|
||||
}
|
||||
119
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/GreedyCacheStrategy.php
vendored
Normal file
119
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/GreedyCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This strategy represents a "greedy" HTTP client.
|
||||
*
|
||||
* It can be used to cache responses in spite of any cache related response headers,
|
||||
* but it SHOULDN'T be used unless absolutely necessary, e.g. when accessing
|
||||
* badly designed APIs without Cache control.
|
||||
*
|
||||
* Obviously, this follows no RFC :(.
|
||||
*/
|
||||
class GreedyCacheStrategy extends PrivateCacheStrategy
|
||||
{
|
||||
const HEADER_TTL = 'X-Kevinrob-GuzzleCache-TTL';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $defaultTtl;
|
||||
|
||||
/**
|
||||
* @var KeyValueHttpHeader
|
||||
*/
|
||||
private $varyHeaders;
|
||||
|
||||
public function __construct(CacheStorageInterface $cache = null, $defaultTtl, KeyValueHttpHeader $varyHeaders = null)
|
||||
{
|
||||
$this->defaultTtl = $defaultTtl;
|
||||
$this->varyHeaders = $varyHeaders;
|
||||
parent::__construct($cache);
|
||||
}
|
||||
|
||||
protected function getCacheKey(RequestInterface $request, KeyValueHttpHeader $varyHeaders = null)
|
||||
{
|
||||
if (null === $varyHeaders || $varyHeaders->isEmpty()) {
|
||||
return hash(
|
||||
'sha256',
|
||||
'greedy'.$request->getMethod().$request->getUri()
|
||||
);
|
||||
}
|
||||
|
||||
$cacheHeaders = [];
|
||||
foreach ($varyHeaders as $key => $value) {
|
||||
if ($request->hasHeader($key)) {
|
||||
$cacheHeaders[$key] = $request->getHeader($key);
|
||||
}
|
||||
}
|
||||
|
||||
return hash(
|
||||
'sha256',
|
||||
'greedy'.$request->getMethod().$request->getUri().json_encode($cacheHeaders)
|
||||
);
|
||||
}
|
||||
|
||||
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$warningMessage = sprintf('%d - "%s" "%s"',
|
||||
299,
|
||||
'Cached although the response headers indicate not to do it!',
|
||||
(new \DateTime())->format(\DateTime::RFC1123)
|
||||
);
|
||||
|
||||
$response = $response->withAddedHeader('Warning', $warningMessage);
|
||||
|
||||
if ($cacheObject = $this->getCacheObject($request, $response)) {
|
||||
return $this->storage->save(
|
||||
$this->getCacheKey($request, $this->varyHeaders),
|
||||
$cacheObject
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getCacheObject(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
if (!array_key_exists($response->getStatusCode(), $this->statusAccepted)) {
|
||||
// Don't cache it
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null !== $this->varyHeaders && $this->varyHeaders->has('*')) {
|
||||
// This will never match with a request
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $response->withoutHeader('Etag')->withoutHeader('Last-Modified');
|
||||
|
||||
$ttl = $this->defaultTtl;
|
||||
if ($request->hasHeader(self::HEADER_TTL)) {
|
||||
$ttlHeaderValues = $request->getHeader(self::HEADER_TTL);
|
||||
$ttl = (int)reset($ttlHeaderValues);
|
||||
}
|
||||
|
||||
return new CacheEntry($request->withoutHeader(self::HEADER_TTL), $response, new \DateTime(sprintf('+%d seconds', $ttl)));
|
||||
}
|
||||
|
||||
public function fetch(RequestInterface $request)
|
||||
{
|
||||
$cache = $this->storage->fetch($this->getCacheKey($request, $this->varyHeaders));
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(RequestInterface $request)
|
||||
{
|
||||
return $this->storage->delete($this->getCacheKey($request));
|
||||
}
|
||||
}
|
||||
43
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/NullCacheStrategy.php
vendored
Normal file
43
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/NullCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class NullCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function fetch(RequestInterface $request)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function update(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(RequestInterface $request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
256
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PrivateCacheStrategy.php
vendored
Normal file
256
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PrivateCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy;
|
||||
|
||||
use Kevinrob\GuzzleCache\CacheEntry;
|
||||
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;
|
||||
use Kevinrob\GuzzleCache\Storage\VolatileRuntimeStorage;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This strategy represents a "private" HTTP client.
|
||||
* Pay attention to share storage between application with caution!
|
||||
*
|
||||
* For example, a response with cache-control header "private, max-age=60"
|
||||
* will be cached by this strategy.
|
||||
*
|
||||
* The rules applied are from RFC 7234.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7234
|
||||
*/
|
||||
class PrivateCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* @var CacheStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected $statusAccepted = [
|
||||
200 => 200,
|
||||
203 => 203,
|
||||
204 => 204,
|
||||
300 => 300,
|
||||
301 => 301,
|
||||
404 => 404,
|
||||
405 => 405,
|
||||
410 => 410,
|
||||
414 => 414,
|
||||
418 => 418,
|
||||
501 => 501,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $ageKey = [
|
||||
'max-age',
|
||||
];
|
||||
|
||||
public function __construct(CacheStorageInterface $cache = null)
|
||||
{
|
||||
$this->storage = $cache !== null ? $cache : new VolatileRuntimeStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return CacheEntry|null entry to save, null if can't cache it
|
||||
*/
|
||||
protected function getCacheObject(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
if (!isset($this->statusAccepted[$response->getStatusCode()])) {
|
||||
// Don't cache it
|
||||
return;
|
||||
}
|
||||
|
||||
$cacheControl = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
|
||||
$varyHeader = new KeyValueHttpHeader($response->getHeader('Vary'));
|
||||
|
||||
if ($varyHeader->has('*')) {
|
||||
// This will never match with a request
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cacheControl->has('no-store')) {
|
||||
// No store allowed (maybe some sensitives data...)
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cacheControl->has('no-cache')) {
|
||||
// Stale response see RFC7234 section 5.2.1.4
|
||||
$entry = new CacheEntry($request, $response, new \DateTime('-1 seconds'));
|
||||
|
||||
return $entry->hasValidationInformation() ? $entry : null;
|
||||
}
|
||||
|
||||
foreach ($this->ageKey as $key) {
|
||||
if ($cacheControl->has($key)) {
|
||||
return new CacheEntry(
|
||||
$request,
|
||||
$response,
|
||||
new \DateTime('+'.(int) $cacheControl->get($key).'seconds')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($response->hasHeader('Expires')) {
|
||||
$expireAt = \DateTime::createFromFormat(\DateTime::RFC1123, $response->getHeaderLine('Expires'));
|
||||
if ($expireAt !== false) {
|
||||
return new CacheEntry(
|
||||
$request,
|
||||
$response,
|
||||
$expireAt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new CacheEntry($request, $response, new \DateTime('-1 seconds'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key for the response cache.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param null|KeyValueHttpHeader $varyHeaders The vary headers which should be honoured by the cache (optional)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey(RequestInterface $request, KeyValueHttpHeader $varyHeaders = null)
|
||||
{
|
||||
if (!$varyHeaders) {
|
||||
return hash('sha256', $request->getMethod().$request->getUri());
|
||||
}
|
||||
|
||||
$cacheHeaders = [];
|
||||
|
||||
foreach ($varyHeaders as $key => $value) {
|
||||
if ($request->hasHeader($key)) {
|
||||
$cacheHeaders[$key] = $request->getHeader($key);
|
||||
}
|
||||
}
|
||||
|
||||
return hash('sha256', $request->getMethod().$request->getUri().json_encode($cacheHeaders));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a CacheEntry or null if no cache.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return CacheEntry|null
|
||||
*/
|
||||
public function fetch(RequestInterface $request)
|
||||
{
|
||||
/** @var int|null $maxAge */
|
||||
$maxAge = null;
|
||||
|
||||
if ($request->hasHeader('Cache-Control')) {
|
||||
$reqCacheControl = new KeyValueHttpHeader($request->getHeader('Cache-Control'));
|
||||
if ($reqCacheControl->has('no-cache')) {
|
||||
// Can't return cache
|
||||
return null;
|
||||
}
|
||||
|
||||
$maxAge = $reqCacheControl->get('max-age', null);
|
||||
} elseif ($request->hasHeader('Pragma')) {
|
||||
$pragma = new KeyValueHttpHeader($request->getHeader('Pragma'));
|
||||
if ($pragma->has('no-cache')) {
|
||||
// Can't return cache
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$cache = $this->storage->fetch($this->getCacheKey($request));
|
||||
if ($cache !== null) {
|
||||
$varyHeaders = $cache->getVaryHeaders();
|
||||
|
||||
// vary headers exist from a previous response, check if we have a cache that matches those headers
|
||||
if (!$varyHeaders->isEmpty()) {
|
||||
$cache = $this->storage->fetch($this->getCacheKey($request, $varyHeaders));
|
||||
|
||||
if (!$cache) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ((string)$cache->getOriginalRequest()->getUri() !== (string)$request->getUri()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($maxAge !== null) {
|
||||
if ($cache->getAge() > $maxAge) {
|
||||
// Cache entry is too old for the request requirements!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$cache->isVaryEquals($request)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return bool true if success
|
||||
*/
|
||||
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$reqCacheControl = new KeyValueHttpHeader($request->getHeader('Cache-Control'));
|
||||
if ($reqCacheControl->has('no-store')) {
|
||||
// No caching allowed
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheObject = $this->getCacheObject($request, $response);
|
||||
if ($cacheObject !== null) {
|
||||
// store the cache against the URI-only key
|
||||
$success = $this->storage->save(
|
||||
$this->getCacheKey($request),
|
||||
$cacheObject
|
||||
);
|
||||
|
||||
$varyHeaders = $cacheObject->getVaryHeaders();
|
||||
|
||||
if (!$varyHeaders->isEmpty()) {
|
||||
// also store the cache against the vary headers based key
|
||||
$success = $this->storage->save(
|
||||
$this->getCacheKey($request, $varyHeaders),
|
||||
$cacheObject
|
||||
);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return bool true if success
|
||||
*/
|
||||
public function update(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
return $this->cache($request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(RequestInterface $request)
|
||||
{
|
||||
return $this->storage->delete($this->getCacheKey($request));
|
||||
}
|
||||
}
|
||||
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PublicCacheStrategy.php
vendored
Normal file
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PublicCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Kevinrob\GuzzleCache\Strategy;
|
||||
|
||||
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This strategy represents a "public" or "shared" HTTP client.
|
||||
* You can share the storage between applications.
|
||||
*
|
||||
* For example, a response with cache-control header "private, max-age=60"
|
||||
* will be NOT cached by this strategy.
|
||||
*
|
||||
* The rules applied are from RFC 7234.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7234
|
||||
*/
|
||||
class PublicCacheStrategy extends PrivateCacheStrategy
|
||||
{
|
||||
public function __construct(CacheStorageInterface $cache = null)
|
||||
{
|
||||
parent::__construct($cache);
|
||||
|
||||
array_unshift($this->ageKey, 's-maxage');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCacheObject(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$cacheControl = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
|
||||
if ($cacheControl->has('private')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::getCacheObject($request, $response);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user