Skip to content

Commit 17d2ce5

Browse files
committed
✨ added http caching
1 parent 7e230f1 commit 17d2ce5

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

src/Cache.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
namespace Leaf\Http;
4+
5+
/**
6+
* Leaf Http Caching
7+
* ------------------------------------
8+
* HTTP Caching made simple with Leaf
9+
*
10+
* @author Michael Darko
11+
* @since 3.0.0
12+
*/
13+
class Cache
14+
{
15+
/**
16+
* Set Last-Modified HTTP Response Header
17+
*
18+
* Set the HTTP 'Last-Modified' header and stop if a conditional
19+
* GET request's `If-Modified-Since` header matches the last modified time
20+
* of the resource. The `time` argument is a UNIX timestamp integer value.
21+
* When the current request includes an 'If-Modified-Since' header that
22+
* matches the specified last modified time, the application will stop
23+
* and send a '304 Not Modified' response to the client.
24+
*
25+
* @param int $time The last modified UNIX timestamp
26+
*/
27+
public static function lastModified(int $time)
28+
{
29+
Headers::set('Last-Modified', gmdate('D, d M Y H:i:s T', $time));
30+
31+
if ($time === strtotime(Headers::get('If-Modified-Since'))) {
32+
\Leaf\App::halt(304);
33+
}
34+
}
35+
36+
/**
37+
* Set ETag HTTP Response Header
38+
*
39+
* Set the etag header and stop if the conditional GET request matches.
40+
* The `value` argument is a unique identifier for the current resource.
41+
* The `type` argument indicates whether the etag should be used as a strong or
42+
* weak cache validator.
43+
*
44+
* When the current request includes an 'If-None-Match' header with
45+
* a matching etag, execution is immediately stopped. If the request
46+
* method is GET or HEAD, a '304 Not Modified' response is sent.
47+
*
48+
* @param string $value The etag value
49+
* @param string $type The type of etag to create; either "strong" or "weak"
50+
*/
51+
public static function etag(string $value, string $type = "strong")
52+
{
53+
if (!in_array($type, ["strong", "weak"])) {
54+
trigger_error("Invalid Leaf::etag type. Expected either \"strong\" or \"weak\".");
55+
}
56+
57+
$value = "\"$value\"";
58+
59+
if ($type === "weak") {
60+
$value = "W/" . $value;
61+
}
62+
63+
Headers::set("ETag", $value);
64+
65+
if ($etagsHeader = Headers::get("If-None-Match")) {
66+
$etags = preg_split("@\s*,\s*@", $etagsHeader);
67+
68+
if (in_array($value, $etags) || in_array("*", $etags)) {
69+
$_304Methods = [Request::METHOD_GET, Request::METHOD_HEAD];
70+
71+
if (in_array(Request::getMethod(), $_304Methods)) {
72+
\Leaf\App::halt(304);
73+
} else {
74+
// according to https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
75+
// all methods besides GET and HEAD should return a 421 (Precondition Failed)
76+
\Leaf\App::halt(412);
77+
}
78+
}
79+
}
80+
}
81+
82+
/**
83+
* Set Expires HTTP response header
84+
*
85+
* The `Expires` header tells the HTTP client the time at which
86+
* the current resource should be considered stale. At that time the HTTP
87+
* client will send a conditional GET request to the server; the server
88+
* may return a 200 OK if the resource has changed, else a 304 Not Modified
89+
* if the resource has not changed. The `Expires` header should be used in
90+
* conjunction with the `etag()` or `lastModified()` methods above.
91+
*
92+
* @param string|int $time If string, a time to be parsed by `strtotime()`; If int, a UNIX timestamp;
93+
*/
94+
public static function expires($time)
95+
{
96+
if (is_string($time)) {
97+
$time = strtotime($time);
98+
}
99+
100+
Headers::set('Expires', gmdate('D, d M Y H:i:s T', $time));
101+
}
102+
}

0 commit comments

Comments
 (0)