CacheControl::setMaxAge()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Bluz Framework Component
5
 *
6
 * @copyright Bluz PHP Team
7
 * @link      https://github.com/bluzphp/framework
8
 */
9
10
declare(strict_types=1);
11
12
namespace Bluz\Http;
13
14
use Bluz\Common\Container\Container;
15
use Bluz\Response\Response;
16
use DateTime;
17
use DateTimeZone;
18
use Exception;
19
20
/**
21
 * HTTP Cache Control
22
 *
23
 * Wrapper for working with HTTP headers
24
 *     - Cache-Control
25
 *     - Last-Modified
26
 *     - Expires
27
 *     - ETag
28
 *     - Age
29
 *
30
 * @package  Bluz\Http
31
 * @author   Anton Shevchuk
32
 * @link     http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
33
 * @link     http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
34
 */
35
class CacheControl
36
{
37
    use Container;
38
39
    /**
40
     * @var Response instance
41
     */
42
    protected $response;
43
44
    /**
45
     * Create instance
46
     *
47
     * @param Response $response
48
     */
49 11
    public function __construct(Response $response)
50
    {
51 11
        $this->response = $response;
52 11
    }
53
54
    /**
55
     * Prepare Cache-Control header
56
     *
57
     * @return void
58
     */
59 7
    protected function updateCacheControlHeader(): void
60
    {
61 7
        $parts = [];
62 7
        ksort($this->container);
63 7
        foreach ($this->container as $key => $value) {
64 7
            if (true === $value) {
65 4
                $parts[] = $key;
66
            } else {
67 5
                if (preg_match('#[^a-zA-Z0-9._-]#', (string)$value)) {
68
                    $value = '"' . $value . '"';
69
                }
70 5
                $parts[] = "$key=$value";
71
            }
72
        }
73 7
        if (count($parts)) {
74 7
            $this->response->setHeader('Cache-Control', implode(', ', $parts));
75
        }
76 7
    }
77
78
    /**
79
     * Marks the response as "private".
80
     *
81
     * It makes the response ineligible for serving other clients.
82
     *
83
     * @return void
84
     */
85 1
    public function setPrivate(): void
86
    {
87 1
        $this->doDeleteContainer('public');
88 1
        $this->doSetContainer('private', true);
89 1
        $this->updateCacheControlHeader();
90 1
    }
91
92
    /**
93
     * Marks the response as "public".
94
     *
95
     * It makes the response eligible for serving other clients.
96
     *
97
     * @return void
98
     */
99 3
    public function setPublic(): void
100
    {
101 3
        $this->doDeleteContainer('private');
102 3
        $this->doSetContainer('public', true);
103 3
        $this->updateCacheControlHeader();
104 3
    }
105
106
    /**
107
     * Returns the number of seconds after the time specified in the response's Date
108
     * header when the response should no longer be considered fresh.
109
     *
110
     * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
111
     * back on an expires header. It returns null when no maximum age can be established.
112
     *
113
     * @return integer|null Number of seconds
114
     */
115 6
    public function getMaxAge(): ?int
116
    {
117 6
        if ($this->doContainsContainer('s-maxage')) {
118 1
            return (int)$this->doGetContainer('s-maxage');
119
        }
120
121 5
        if ($this->doContainsContainer('max-age')) {
122 2
            return (int)$this->doGetContainer('max-age');
123
        }
124
125 3
        if ($expires = $this->getExpires()) {
126 1
            $expires = DateTime::createFromFormat(DATE_RFC2822, $expires);
127 1
            return (int) $expires->format('U') - date('U');
128
        }
129
130 2
        return null;
131
    }
132
133
    /**
134
     * Sets the number of seconds after which the response should no longer be considered fresh.
135
     *
136
     * This methods sets the Cache-Control max-age directive.
137
     *
138
     * @param integer $value Number of seconds
139
     *
140
     * @return void
141
     */
142 3
    public function setMaxAge(int $value): void
143
    {
144 3
        $this->doSetContainer('max-age', $value);
145 3
        $this->updateCacheControlHeader();
146 3
    }
147
148
    /**
149
     * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
150
     *
151
     * This methods sets the Cache-Control s-maxage directive.
152
     *
153
     * @param integer $value Number of seconds
154
     *
155
     * @return void
156
     */
157 2
    public function setSharedMaxAge(int $value): void
158
    {
159 2
        $this->setPublic();
160 2
        $this->doSetContainer('s-maxage', $value);
161 2
        $this->updateCacheControlHeader();
162 2
    }
163
164
    /**
165
     * Returns the response's time-to-live in seconds.
166
     *
167
     * It returns null when no freshness information is present in the response.
168
     * When the responses TTL is <= 0, the response may not be served from cache without first
169
     * revalidating with the origin.
170
     *
171
     * @return integer|null The TTL in seconds
172
     */
173 2
    public function getTtl(): ?int
174
    {
175 2
        if ($maxAge = $this->getMaxAge()) {
176 1
            return $maxAge - $this->getAge();
177
        }
178 1
        return null;
179
    }
180
181
    /**
182
     * Sets the response's time-to-live for shared caches.
183
     *
184
     * This method adjusts the Cache-Control/s-maxage directive.
185
     *
186
     * @param integer $seconds Number of seconds
187
     *
188
     * @return void
189
     */
190 1
    public function setTtl(int $seconds): void
191
    {
192 1
        $this->setSharedMaxAge($this->getAge() + $seconds);
193 1
    }
194
195
    /**
196
     * Sets the response's time-to-live for private/client caches.
197
     *
198
     * This method adjusts the Cache-Control/max-age directive.
199
     *
200
     * @param integer $seconds Number of seconds
201
     *
202
     * @return void
203
     */
204 1
    public function setClientTtl(int $seconds): void
205
    {
206 1
        $this->setMaxAge($this->getAge() + $seconds);
207 1
    }
208
209
    /**
210
     * Returns the literal value of the ETag HTTP header
211
     *
212
     * @return string The ETag HTTP header or null if it does not exist
213
     */
214 1
    public function getEtag(): string
215
    {
216 1
        return $this->response->getHeader('ETag');
217
    }
218
219
    /**
220
     * Sets the ETag value
221
     *
222
     * @param string $etag The ETag unique identifier
223
     * @param bool $weak Whether you want a weak ETag or not
224
     *
225
     * @return void
226
     */
227 1
    public function setEtag(string $etag, bool $weak = false): void
228
    {
229 1
        $etag = trim($etag, '"');
230 1
        $this->response->setHeader('ETag', (true === $weak ? 'W/' : '') . '"' . $etag . '"');
231 1
    }
232
233
    /**
234
     * Returns the age of the response
235
     *
236
     * @return integer The age of the response in seconds
237
     */
238 3
    public function getAge(): int
239
    {
240 3
        if ($age = $this->response->getHeader('Age')) {
241
            return (int)$age;
242
        }
243 3
        return max(time() - date('U'), 0);
244
    }
245
246
    /**
247
     * Set the age of the response
248
     *
249
     * @param integer $age
250
     *
251
     * @return void
252
     */
253
    public function setAge(int $age): void
254
    {
255
        $this->response->setHeader('Age', $age);
256
    }
257
258
    /**
259
     * Returns the value of the Expires header as a DateTime instance
260
     *
261
     * @return string A string or null if the header does not exist
262
     */
263 3
    public function getExpires(): string
264
    {
265 3
        return $this->response->getHeader('Expires');
266
    }
267
268
    /**
269
     * Sets the Expires HTTP header with a DateTime instance
270
     *
271
     * @param DateTime|string $date A \DateTime instance or date as string
272
     *
273
     * @return void
274
     * @throws Exception
275
     */
276 1
    public function setExpires($date): void
277
    {
278 1
        if ($date instanceof DateTime) {
279
            $date = clone $date;
280
        } else {
281 1
            $date = new DateTime($date);
282
        }
283
284 1
        $date->setTimezone(new DateTimeZone('UTC'));
285 1
        $this->response->setHeader('Expires', $date->format('D, d M Y H:i:s') . ' GMT');
286 1
    }
287
288
    /**
289
     * Returns the Last-Modified HTTP header as a string
290
     *
291
     * @return string A string or null if the header does not exist
292
     */
293 1
    public function getLastModified(): string
294
    {
295 1
        return $this->response->getHeader('Last-Modified');
296
    }
297
298
    /**
299
     * Sets the Last-Modified HTTP header with a DateTime instance or string
300
     *
301
     * @param  DateTime|string $date A \DateTime instance or date as string
302
     *
303
     * @return void
304
     * @throws Exception
305
     */
306 1
    public function setLastModified($date): void
307
    {
308 1
        if ($date instanceof DateTime) {
309
            $date = clone $date;
310
        } else {
311 1
            $date = new DateTime($date);
312
        }
313
314 1
        $date->setTimezone(new DateTimeZone('UTC'));
315 1
        $this->response->setHeader('Last-Modified', $date->format('D, d M Y H:i:s') . ' GMT');
316 1
    }
317
318
    /**
319
     * Marks the response stale by setting the Age header to be equal to the maximum age of the response
320
     *
321
     * @return void
322
     */
323
    public function expire(): void
324
    {
325
        if ($this->getTtl() > 0) {
326
            $this->setAge($this->getMaxAge());
0 ignored issues
show
Bug introduced by
It seems like $this->getMaxAge() can also be of type null; however, parameter $age of Bluz\Http\CacheControl::setAge() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
            $this->setAge(/** @scrutinizer ignore-type */ $this->getMaxAge());
Loading history...
327
        }
328
    }
329
}
330