Passed
Push — master ( 8f1854...f978b8 )
by Kevin
02:57
created

CacheEntry::__wakeup()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Kevinrob\GuzzleCache;
4
5
use Psr\Http\Message\RequestInterface;
6
use Psr\Http\Message\ResponseInterface;
7
8
class CacheEntry
9
{
10
    /**
11
     * @var RequestInterface
12
     */
13
    protected $request;
14
15
    /**
16
     * @var ResponseInterface
17
     */
18
    protected $response;
19
20
    /**
21
     * This field is only used for serialize.
22
     * Response::body is a stream and can't be serialized.
23
     *
24
     * @var string
25
     */
26
    protected $responseBody;
27
28
    /**
29
     * @var \DateTime
30
     */
31
    protected $staleAt;
32
33
    /**
34
     * @var \DateTime
35
     */
36
    protected $staleIfErrorTo;
37
38
    /**
39
     * @var \DateTime
40
     */
41
    protected $staleWhileRevalidateTo;
42
43
    /**
44
     * @var \DateTime
45
     */
46
    protected $dateCreated;
47
48
    /**
49
     * Cached timestamp of staleAt variable.
50
     *
51
     * @var int
52
     */
53
    protected $timestampStale;
54
55
    /**
56
     * @param RequestInterface $request
57
     * @param ResponseInterface $response
58
     * @param \DateTime $staleAt
59
     * @param \DateTime|null $staleIfErrorTo if null, detected with the headers (RFC 5861)
60
     * @param \DateTime|null $staleWhileRevalidateTo
61
     */
62 43
    public function __construct(
63
        RequestInterface $request,
64
        ResponseInterface $response,
65
        \DateTime $staleAt,
66
        \DateTime $staleIfErrorTo = null,
67
        \DateTime $staleWhileRevalidateTo = null
68
    ) {
69 43
        $this->dateCreated = new \DateTime();
70
71 43
        $this->request = $request;
72 43
        $this->response = $response;
73 43
        $this->staleAt = $staleAt;
74
75 43
        $values = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
76
77 43
        if ($staleIfErrorTo === null && $values->has('stale-if-error')) {
78 3
            $this->staleIfErrorTo = (new \DateTime(
79 3
                '@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-if-error'))
80 3
            ));
81 3
        } else {
82 40
            $this->staleIfErrorTo = $staleIfErrorTo;
83
        }
84
85 43
        if ($staleWhileRevalidateTo === null && $values->has('stale-while-revalidate')) {
86 4
            $this->staleWhileRevalidateTo = new \DateTime(
87 4
                '@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-while-revalidate'))
88 4
            );
89 4
        } else {
90 40
            $this->staleWhileRevalidateTo = $staleWhileRevalidateTo;
91
        }
92 43
    }
93
94
    /**
95
     * @return ResponseInterface
96
     */
97 36
    public function getResponse()
98
    {
99 36
        return $this->response
100 36
            ->withHeader('Age', $this->getAge());
101
    }
102
103
    /**
104
     * @return ResponseInterface
105
     */
106 2
    public function getOriginalResponse()
107
    {
108 2
        return $this->response;
109
    }
110
111
    /**
112
     * @return RequestInterface
113
     */
114 28
    public function getOriginalRequest()
115
    {
116 28
        return $this->request;
117
    }
118
119
    /**
120
     * @param RequestInterface $request
121
     * @return bool
122
     */
123 28
    public function isVaryEquals(RequestInterface $request)
124
    {
125 28
        if ($this->response->hasHeader('Vary')) {
126 3
            if ($this->request === null) {
127
                return false;
128
            }
129
130 3
            foreach ($this->getVaryHeaders() as $key => $value) {
131 3
                if (!$this->request->hasHeader($key)
132 3
                    && !$request->hasHeader($key)
133 3
                ) {
134
                    // Absent from both
135 3
                    continue;
136 3
                } elseif ($this->request->getHeaderLine($key)
137 3
                    == $request->getHeaderLine($key)
138 3
                ) {
139
                    // Same content
140 3
                    continue;
141
                }
142
143
                return false;
144 3
            }
145 3
        }
146
147 28
        return true;
148
    }
149
150
    /**
151
     * Get the vary headers that should be honoured by the cache.
152
     *
153
     * @return KeyValueHttpHeader
154
     */
155 30
    public function getVaryHeaders()
156
    {
157 30
        return new KeyValueHttpHeader($this->response->getHeader('Vary'));
158
    }
159
160
    /**
161
     * @return \DateTime
162
     */
163
    public function getStaleAt()
164
    {
165
        return $this->staleAt;
166
    }
167
168
    /**
169
     * @return bool
170
     */
171 34
    public function isFresh()
172
    {
173 34
        return !$this->isStale();
174
    }
175
176
    /**
177
     * @return bool
178
     */
179 34
    public function isStale()
180
    {
181 34
        return $this->getStaleAge() > 0;
182
    }
183
184
    /**
185
     * @return int positive value equal staled
186
     */
187 34
    public function getStaleAge()
188
    {
189
        // This object is immutable
190 34
        if ($this->timestampStale === null) {
191 34
            $this->timestampStale = $this->staleAt->getTimestamp();
192 34
        }
193
194 34
        return time() - $this->timestampStale;
195
    }
196
197
    /**
198
     * @return bool
199
     */
200 1
    public function serveStaleIfError()
201
    {
202 1
        return $this->staleIfErrorTo !== null
203 1
            && $this->staleIfErrorTo->getTimestamp() >= (new \DateTime())->getTimestamp();
204
    }
205
206
    /**
207
     * @return bool
208
     */
209 4
    public function staleWhileValidate()
210
    {
211 4
        return $this->staleWhileRevalidateTo !== null
212 4
            && $this->staleWhileRevalidateTo->getTimestamp() >= (new \DateTime())->getTimestamp();
213
    }
214
215
    /**
216
     * @return bool
217
     */
218 22
    public function hasValidationInformation()
219
    {
220 22
        return $this->response->hasHeader('Etag') || $this->response->hasHeader('Last-Modified');
221
    }
222
223
    /**
224
     * Time in seconds how long the entry should be kept in the cache
225
     *
226
     * This will not give the time (in seconds) that the response will still be fresh for
227
     * from the HTTP point of view, but an upper bound on how long it is necessary and
228
     * reasonable to keep the response in a cache (to re-use it or re-validate it later on).
229
     *
230
     * @return int TTL in seconds (0 = infinite)
231
     */
232 7
    public function getTTL()
233
    {
234 7
        if ($this->hasValidationInformation()) {
235
            // No TTL if we have a way to re-validate the cache
236 1
            return 0;
237
        }
238
239 6
        $ttl = 0;
240
241
        // Keep it when stale if error
242 6
        if ($this->staleIfErrorTo !== null) {
243 2
            $ttl = max($ttl, $this->staleIfErrorTo->getTimestamp() - time());
244 2
        }
245
246
        // Keep it when stale-while-revalidate
247 6
        if ($this->staleWhileRevalidateTo !== null) {
248 2
            $ttl = max($ttl, $this->staleWhileRevalidateTo->getTimestamp() - time());
249 2
        }
250
251
        // Keep it until it become stale
252 6
        $ttl = max($ttl, $this->staleAt->getTimestamp() - time());
253
254
        // Don't return 0, it's reserved for infinite TTL
255 6
        return $ttl !== 0 ? (int) $ttl : -1;
256
    }
257
258
    /**
259
     * @return int Age in seconds
260
     */
261 36
    public function getAge()
262
    {
263 36
        return time() - $this->dateCreated->getTimestamp();
264
    }
265
266 2
    public function __sleep()
267
    {
268
        // Stream/Resource can't be serialized... So we copy the content
269 2
        if ($this->response !== null) {
270 2
            $this->responseBody = (string) $this->response->getBody();
271 2
            $this->response->getBody()->rewind();
272 2
        }
273
274 2
        return array_keys(get_object_vars($this));
275
    }
276
277 2
    public function __wakeup()
278
    {
279
        // We re-create the stream of the response
280 2
        if ($this->response !== null) {
281 2
            $this->response = $this->response
282 2
                ->withBody(
283 2
                    \GuzzleHttp\Psr7\stream_for($this->responseBody)
284 2
                );
285 2
        }
286 2
    }
287
}
288