Completed
Push — master ( a4c193...4ecd26 )
by Anton
10s
created

CacheControl::updateCacheControlHeader()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0144

Importance

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