Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace Kevinrob\GuzzleCache;
4
 
5
use GuzzleHttp\Psr7\PumpStream;
1441 ariadna 6
use Psr\Http\Message\MessageInterface;
1 efrain 7
use Psr\Http\Message\RequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
 
1441 ariadna 10
class CacheEntry implements \Serializable
1 efrain 11
{
12
    /**
13
     * @var RequestInterface
14
     */
15
    protected $request;
16
 
17
    /**
18
     * @var ResponseInterface
19
     */
20
    protected $response;
21
 
22
    /**
23
     * @var \DateTime
24
     */
25
    protected $staleAt;
26
 
27
    /**
28
     * @var \DateTime
29
     */
30
    protected $staleIfErrorTo;
31
 
32
    /**
33
     * @var \DateTime
34
     */
35
    protected $staleWhileRevalidateTo;
36
 
37
    /**
38
     * @var \DateTime
39
     */
40
    protected $dateCreated;
41
 
42
    /**
43
     * Cached timestamp of staleAt variable.
44
     *
45
     * @var int
46
     */
47
    protected $timestampStale;
48
 
49
    /**
50
     * @param RequestInterface $request
51
     * @param ResponseInterface $response
52
     * @param \DateTime $staleAt
53
     * @param \DateTime|null $staleIfErrorTo if null, detected with the headers (RFC 5861)
54
     * @param \DateTime|null $staleWhileRevalidateTo
55
     */
56
    public function __construct(
57
        RequestInterface $request,
58
        ResponseInterface $response,
59
        \DateTime $staleAt,
1441 ariadna 60
        ?\DateTime $staleIfErrorTo = null,
61
        ?\DateTime $staleWhileRevalidateTo = null
1 efrain 62
    ) {
63
        $this->dateCreated = new \DateTime();
64
 
65
        $this->request = $request;
66
        $this->response = $response;
67
        $this->staleAt = $staleAt;
68
 
69
        $values = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
70
 
71
        if ($staleIfErrorTo === null && $values->has('stale-if-error')) {
72
            $this->staleIfErrorTo = (new \DateTime(
73
                '@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-if-error'))
74
            ));
75
        } else {
76
            $this->staleIfErrorTo = $staleIfErrorTo;
77
        }
78
 
79
        if ($staleWhileRevalidateTo === null && $values->has('stale-while-revalidate')) {
80
            $this->staleWhileRevalidateTo = new \DateTime(
81
                '@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-while-revalidate'))
82
            );
83
        } else {
84
            $this->staleWhileRevalidateTo = $staleWhileRevalidateTo;
85
        }
86
    }
87
 
88
    /**
89
     * @return ResponseInterface
90
     */
91
    public function getResponse()
92
    {
93
        return $this->response
94
            ->withHeader('Age', $this->getAge());
95
    }
96
 
97
    /**
98
     * @return ResponseInterface
99
     */
100
    public function getOriginalResponse()
101
    {
102
        return $this->response;
103
    }
104
 
105
    /**
106
     * @return RequestInterface
107
     */
108
    public function getOriginalRequest()
109
    {
110
        return $this->request;
111
    }
112
 
113
    /**
114
     * @param RequestInterface $request
115
     * @return bool
116
     */
117
    public function isVaryEquals(RequestInterface $request)
118
    {
119
        if ($this->response->hasHeader('Vary')) {
120
            if ($this->request === null) {
121
                return false;
122
            }
123
 
124
            foreach ($this->getVaryHeaders() as $key => $value) {
125
                if (!$this->request->hasHeader($key)
126
                    && !$request->hasHeader($key)
127
                ) {
128
                    // Absent from both
129
                    continue;
130
                } elseif ($this->request->getHeaderLine($key)
131
                    == $request->getHeaderLine($key)
132
                ) {
133
                    // Same content
134
                    continue;
135
                }
136
 
137
                return false;
138
            }
139
        }
140
 
141
        return true;
142
    }
143
 
144
    /**
145
     * Get the vary headers that should be honoured by the cache.
146
     *
147
     * @return KeyValueHttpHeader
148
     */
149
    public function getVaryHeaders()
150
    {
151
        return new KeyValueHttpHeader($this->response->getHeader('Vary'));
152
    }
153
 
154
    /**
155
     * @return \DateTime
156
     */
157
    public function getStaleAt()
158
    {
159
        return $this->staleAt;
160
    }
161
 
162
    /**
163
     * @return bool
164
     */
165
    public function isFresh()
166
    {
167
        return !$this->isStale();
168
    }
169
 
170
    /**
171
     * @return bool
172
     */
173
    public function isStale()
174
    {
175
        return $this->getStaleAge() > 0;
176
    }
177
 
178
    /**
179
     * @return int positive value equal staled
180
     */
181
    public function getStaleAge()
182
    {
183
        // This object is immutable
184
        if ($this->timestampStale === null) {
185
            $this->timestampStale = $this->staleAt->getTimestamp();
186
        }
187
 
188
        return time() - $this->timestampStale;
189
    }
190
 
191
    /**
192
     * @return bool
193
     */
194
    public function serveStaleIfError()
195
    {
196
        return $this->staleIfErrorTo !== null
197
            && $this->staleIfErrorTo->getTimestamp() >= (new \DateTime())->getTimestamp();
198
    }
199
 
200
    /**
201
     * @return bool
202
     */
203
    public function staleWhileValidate()
204
    {
205
        return $this->staleWhileRevalidateTo !== null
206
            && $this->staleWhileRevalidateTo->getTimestamp() >= (new \DateTime())->getTimestamp();
207
    }
208
 
209
    /**
210
     * @return bool
211
     */
212
    public function hasValidationInformation()
213
    {
214
        return $this->response->hasHeader('Etag') || $this->response->hasHeader('Last-Modified');
215
    }
216
 
217
    /**
218
     * Time in seconds how long the entry should be kept in the cache
219
     *
220
     * This will not give the time (in seconds) that the response will still be fresh for
221
     * from the HTTP point of view, but an upper bound on how long it is necessary and
222
     * reasonable to keep the response in a cache (to re-use it or re-validate it later on).
223
     *
224
     * @return int TTL in seconds (0 = infinite)
225
     */
226
    public function getTTL()
227
    {
228
        if ($this->hasValidationInformation()) {
229
            // No TTL if we have a way to re-validate the cache
230
            return 0;
231
        }
232
 
233
        $ttl = 0;
234
 
235
        // Keep it when stale if error
236
        if ($this->staleIfErrorTo !== null) {
237
            $ttl = max($ttl, $this->staleIfErrorTo->getTimestamp() - time());
238
        }
239
 
240
        // Keep it when stale-while-revalidate
241
        if ($this->staleWhileRevalidateTo !== null) {
242
            $ttl = max($ttl, $this->staleWhileRevalidateTo->getTimestamp() - time());
243
        }
244
 
245
        // Keep it until it become stale
246
        $ttl = max($ttl, $this->staleAt->getTimestamp() - time());
247
 
248
        // Don't return 0, it's reserved for infinite TTL
249
        return $ttl !== 0 ? (int) $ttl : -1;
250
    }
251
 
252
    /**
253
     * @return int Age in seconds
254
     */
255
    public function getAge()
256
    {
257
        return time() - $this->dateCreated->getTimestamp();
258
    }
259
 
1441 ariadna 260
    public function __serialize(): array
1 efrain 261
    {
1441 ariadna 262
        return [
263
            'request' => self::toSerializeableMessage($this->request),
264
            'response' => $this->response !== null ? self::toSerializeableMessage($this->response) : null,
265
            'staleAt' => $this->staleAt,
266
            'staleIfErrorTo' => $this->staleIfErrorTo,
267
            'staleWhileRevalidateTo' => $this->staleWhileRevalidateTo,
268
            'dateCreated' => $this->dateCreated,
269
            'timestampStale' => $this->timestampStale,
270
        ];
271
    }
272
 
273
    public function __unserialize(array $data): void
274
    {
275
        $prefix = '';
276
        if (isset($data["\0*\0request"])) {
277
            // We are unserializing a cache entry which was serialized with a version < 4.1.1
278
            $prefix = "\0*\0";
1 efrain 279
        }
1441 ariadna 280
        $this->request = self::restoreStreamBody($data[$prefix.'request']);
281
        $this->response = $data[$prefix.'response'] !== null ? self::restoreStreamBody($data[$prefix.'response']) : null;
282
        $this->staleAt = $data[$prefix.'staleAt'];
283
        $this->staleIfErrorTo = $data[$prefix.'staleIfErrorTo'];
284
        $this->staleWhileRevalidateTo = $data[$prefix.'staleWhileRevalidateTo'];
285
        $this->dateCreated = $data[$prefix.'dateCreated'];
286
        $this->timestampStale = $data[$prefix.'timestampStale'];
287
    }
1 efrain 288
 
1441 ariadna 289
    /**
290
     * Stream/Resource can't be serialized... So we copy the content into an implementation of `Psr\Http\Message\StreamInterface`
291
     *
292
     * @template T of MessageInterface
293
     *
294
     * @param T $message
295
     * @return T
296
     */
297
    private static function toSerializeableMessage(MessageInterface $message): MessageInterface
298
    {
299
        $bodyString = (string)$message->getBody();
300
 
301
        return $message->withBody(
1 efrain 302
            new PumpStream(
1441 ariadna 303
                new BodyStore($bodyString),
1 efrain 304
                [
1441 ariadna 305
                    'size' => mb_strlen($bodyString),
1 efrain 306
                ]
307
            )
308
        );
1441 ariadna 309
    }
1 efrain 310
 
1441 ariadna 311
    /**
312
     * @template T of MessageInterface
313
     *
314
     * @param T $message
315
     * @return T
316
     */
317
    private static function restoreStreamBody(MessageInterface $message): MessageInterface
318
    {
319
        return $message->withBody(
320
            \GuzzleHttp\Psr7\Utils::streamFor((string) $message->getBody())
321
        );
1 efrain 322
    }
323
 
1441 ariadna 324
    public function serialize()
1 efrain 325
    {
1441 ariadna 326
        return serialize($this->__serialize());
1 efrain 327
    }
328
 
1441 ariadna 329
    public function unserialize($data)
330
    {
331
        $this->__unserialize(unserialize($data));
332
    }
1 efrain 333
}