-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathCachedClient.php
125 lines (94 loc) · 2.71 KB
/
CachedClient.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?php
declare(strict_types=1);
namespace Milo\Github\Http;
use Milo\Github;
use Milo\Github\Storages;
/**
* Caching for HTTP clients.
*
* @author Miloslav Hůla (https://github.com/milo)
*/
class CachedClient implements IClient
{
use Github\Strict;
private IClient $client;
/** @var ?callable(Response $response): void */
private $onResponse;
/**
* @param bool $forbidRecheck Forbid checking GitHub for new data; more or less development purpose only
*/
public function __construct(
private Storages\ICache $cache,
IClient $client = null,
private bool $forbidRecheck = false,
) {
$this->client = $client ?: Github\Helpers::createDefaultClient();
}
public function getInnerClient(): IClient
{
return $this->client;
}
/**
* @throws BadResponseException
*/
public function request(Request $request): Response
{
$request = clone $request;
$cacheKey = implode('.', [
$request->getMethod(),
$request->getUrl(),
/** @todo This should depend on Vary: header */
$request->getHeader('Accept'),
$request->getHeader('Accept-Encoding'),
$request->getHeader('Authorization')
]);
if ($cached = $this->cache->load($cacheKey)) {
if ($this->forbidRecheck) {
$cached = clone $cached;
$this->onResponse && call_user_func($this->onResponse, $cached);
return $cached;
}
/** @var $cached Response */
if ($cached->hasHeader('Last-Modified')) {
$request->addHeader('If-Modified-Since', $cached->getHeader('Last-Modified'));
}
if ($cached->hasHeader('ETag')) {
$request->addHeader('If-None-Match', $cached->getHeader('ETag'));
}
}
$response = $this->client->request($request);
if ($this->isCacheable($response)) {
$this->cache->save($cacheKey, clone $response);
}
if (isset($cached) && $response->getCode() === Response::S304_NOT_MODIFIED) {
$cached = clone $cached;
/** @todo Should be responses somehow combined into one? */
$response = $cached->setPrevious($response);
}
$this->onResponse && call_user_func($this->onResponse, $response);
return $response;
}
/** @inheritdoc */
public function onRequest(?callable $callback): static
{
$this->client->onRequest($callback);
return $this;
}
/** @inheritdoc */
public function onResponse(?callable $callback): static
{
$this->client->onResponse(null);
$this->onResponse = $callback;
return $this;
}
protected function isCacheable(Response $response): bool
{
/** @todo Do it properly. Vary:, Pragma:, TTL... */
if (!$response->isCode(200)) {
return false;
} elseif (preg_match('#max-age=0|must-revalidate#i', $response->getHeader('Cache-Control', ''))) {
return false;
}
return $response->hasHeader('ETag') || $response->hasHeader('Last-Modified');
}
}