Completed
Push — master ( e2ec8c...919426 )
by Kevin
04:27
created

CacheEntry::getStaleAge()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
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
    public function __construct(
63
        RequestInterface $request,
64
        ResponseInterface $response,
65
        \DateTime $staleAt,
66
        \DateTime $staleIfErrorTo = null,
67
        \DateTime $staleWhileRevalidateTo = null
68
    ) {
69
        $this->dateCreated = new \DateTime();
70
71
        $this->request = $request;
72
        $this->response = $response;
73
        $this->staleAt = $staleAt;
74
75
        $values = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
76
77
        if ($staleIfErrorTo === null && $values->has('stale-if-error')) {
78
            $this->staleIfErrorTo = (new \DateTime(
79
                '@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-if-error'))
80
            ));
81
        } else {
82
            $this->staleIfErrorTo = $staleIfErrorTo;
83
        }
84
85
        if ($staleWhileRevalidateTo === null && $values->has('stale-while-revalidate')) {
86
            $this->staleWhileRevalidateTo = new \DateTime(
87
                '@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-while-revalidate'))
88
            );
89
        } else {
90
            $this->staleWhileRevalidateTo = $staleWhileRevalidateTo;
91
        }
92
    }
93
94
    /**
95
     * @return ResponseInterface
96
     */
97
    public function getResponse()
98
    {
99
        return $this->response
100
            ->withHeader('Age', $this->getAge());
101
    }
102
103
    /**
104
     * @return ResponseInterface
105
     */
106
    public function getOriginalResponse()
107
    {
108
        return $this->response;
109
    }
110
111
    /**
112
     * @return RequestInterface
113
     */
114
    public function getOriginalRequest()
115
    {
116
        return $this->request;
117
    }
118
119
    /**
120
     * @param RequestInterface $request
121
     * @return bool
122
     */
123
    public function isVaryEquals(RequestInterface $request)
124
    {
125
        if ($this->response->hasHeader('Vary')) {
126
            if ($this->request === null) {
127
                return false;
128
            }
129
130
            $varyHeaders = new KeyValueHttpHeader($this->response->getHeader('Vary'));
131
132
            foreach ($varyHeaders as $key => $value) {
133
                if (!$this->request->hasHeader($key)
134
                    && !$request->hasHeader($key)
135
                ) {
136
                    // Absent from both
137
                    continue;
138
                } elseif ($this->request->getHeaderLine($key)
139
                    == $request->getHeaderLine($key)
140
                ) {
141
                    // Same content
142
                    continue;
143
                }
144
145
                return false;
146
            }
147
        }
148
149
        return true;
150
    }
151
152
    /**
153
     * @return \DateTime
154
     */
155
    public function getStaleAt()
156
    {
157
        return $this->staleAt;
158
    }
159
160
    /**
161
     * @return bool
162
     */
163
    public function isFresh()
164
    {
165
        return !$this->isStale();
166
    }
167
168
    /**
169
     * @return bool
170
     */
171
    public function isStale()
172
    {
173
        return $this->getStaleAge() > 0;
174
    }
175
176
    /**
177
     * @return int positive value equal staled
178
     */
179
    public function getStaleAge()
180
    {
181
        // This object is immutable
182
        if ($this->timestampStale === null) {
183
            $this->timestampStale = $this->staleAt->getTimestamp();
184
        }
185
186
        return time() - $this->timestampStale;
187
    }
188
189
    /**
190
     * @return bool
191
     */
192
    public function serveStaleIfError()
193
    {
194
        return $this->staleIfErrorTo !== null
195
            && $this->staleIfErrorTo->getTimestamp() >= (new \DateTime())->getTimestamp();
196
    }
197
198
    /**
199
     * @return bool
200
     */
201
    public function staleWhileValidate()
202
    {
203
        return $this->staleWhileRevalidateTo !== null
204
            && $this->staleWhileRevalidateTo->getTimestamp() >= (new \DateTime())->getTimestamp();
205
    }
206
207
    /**
208
     * @return bool
209
     */
210
    public function hasValidationInformation()
211
    {
212
        return $this->response->hasHeader('Etag') || $this->response->hasHeader('Last-Modified');
213
    }
214
215
    /**
216
     * @return int TTL in seconds (0 = infinite)
217
     */
218
    public function getTTL()
219
    {
220
        if ($this->hasValidationInformation()) {
221
            // No TTL if we have a way to re-validate the cache
222
            return 0;
223
        }
224
225
        if ($this->staleIfErrorTo !== null) {
226
            // Keep it when stale if error
227
            $ttl = $this->staleIfErrorTo->getTimestamp() - time();
228
        } else {
229
            // Keep it until it become stale
230
            $ttl = $this->staleAt->getTimestamp() - time();
231
        }
232
233
        // Don't return 0, it's reserved for infinite TTL
234
        return $ttl !== 0 ? (int) $ttl : -1;
235
    }
236
237
    /**
238
     * @return int Age in seconds
239
     */
240
    public function getAge()
241
    {
242
        return time() - $this->dateCreated->getTimestamp();
243
    }
244
245
    public function __sleep()
246
    {
247
        // Stream/Resource can't be serialized... So we copy the content
248
        if ($this->response !== null) {
249
            $this->responseBody = (string) $this->response->getBody();
250
            $this->response->getBody()->rewind();
251
        }
252
253
        return array_keys(get_object_vars($this));
254
    }
255
256
    public function __wakeup()
257
    {
258
        // We re-create the stream of the response
259
        if ($this->response !== null) {
260
            $this->response = $this->response
261
                ->withBody(
262
                    \GuzzleHttp\Psr7\stream_for($this->responseBody)
263
                );
264
        }
265
    }
266
}
267