Completed
Push — master ( 42c1ef...c54fd2 )
by Kevin
04:21
created

CacheEntry::__sleep()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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