PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.4.0
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.4.0
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / nitropack-sdk / NitroPack / SDK / Pagecache.php
nitropack / nitropack-sdk / NitroPack / SDK Last commit date
Api 6 years ago Integrations 6 years ago StorageDriver 5 years ago Url 6 years ago data 6 years ago Api.php 6 years ago Crypto.php 6 years ago Device.php 6 years ago DeviceType.php 6 years ago EmptyConfigException.php 6 years ago Filesystem.php 5 years ago IntegrationUrl.php 6 years ago NitroPack.php 5 years ago Pagecache.php 5 years ago PurgeType.php 6 years ago StorageException.php 6 years ago Website.php 6 years ago
Pagecache.php
274 lines
1 <?php
2 namespace NitroPack\SDK;
3
4 class Pagecache {
5 protected $url;
6 protected $cookies;
7 protected $supportedCookies;
8 protected $dataDir;
9 protected $isAjax;
10 protected $referer;
11 protected $parent;
12 protected $Device;
13 protected $useCompression;
14 protected $useInvalidated;
15
16 public static function getUrlDir($dataDir, $url, $useInvalidated = false) {
17 $safeUrl = str_replace(array('/','?',':',';','=','&','.','--','%','~'),'-', $url);
18 if (defined('NITRO_DEBUG_MODE') && NITRO_DEBUG_MODE) {
19 $urlDir = $dataDir . "/" . $safeUrl;
20 } else {
21 $urlDir = $dataDir . "/" . md5($safeUrl);
22 }
23
24 if ($useInvalidated) {
25 return $urlDir . "_i";
26 } else {
27 return $urlDir;
28 }
29 }
30
31 public function __construct($url, $userAgent, $cookies = array(), $supportedCookies = array(), $isAjax = false, $referer = NULL) {
32 $this->url = new \NitroPack\Url($url);
33 $this->cookies = $cookies;
34 $this->supportedCookies = $supportedCookies;
35 $this->dataDir = NULL;
36 $this->isAjax = $isAjax;
37 $this->Device = new Device($userAgent);
38 $this->useCompression = false;
39 $this->useInvalidated = false;
40 $this->setReferer($referer);
41 }
42
43 public function enableCompression() {
44 $this->useCompression = true;
45 if ($this->parent) {
46 $this->parent->enableCompression();
47 }
48 }
49
50 public function disableCompression() {
51 $this->useCompression = false;
52 if ($this->parent) {
53 $this->parent->disableCompression();
54 }
55 }
56
57 public function useInvalidated($useInvalidated) {
58 $this->useInvalidated = $useInvalidated;
59 if ($this->parent) {
60 $this->parent->useInvalidated($useInvalidated);
61 }
62 }
63
64 public function getUseInvalidated() {
65 return $this->useInvalidated;
66 }
67
68 public function setReferer($referer) {
69 $this->referer = $referer ? new \NitroPack\Url($referer) : NULL;
70 if ($this->referer) {
71 $this->parent = new Pagecache($this->referer->getUrl(), $this->Device->getUserAgent(), $this->cookies, $this->supportedCookies);
72 } else {
73 $this->parent = NULL;
74 }
75 }
76
77 public function getReferer() {
78 return $this->referer;
79 }
80
81 public function getParent() {
82 return $this->parent;
83 }
84
85 public function setDataDir($dir) {
86 if ($dir === null) {
87 $this->dataDir = null;
88 } else {
89 $this->dataDir = $dir . "/" . $this->Device->getType();
90 }
91
92 if ($this->parent) {
93 $this->parent->setDataDir($dir);
94 }
95 }
96
97 public function hasCache() {
98 // If there is no cache for the parent we do not need to check cache existance for the current object, because we only serve AJAX cache for pages already cached by NitroPack
99 if ($this->parent && !$this->parent->hasCache()) return false;
100 if ($this->useInvalidated) {
101 $this->convertToStaleCache();
102 return Filesystem::fileExists($this->getCachefilePath("stale"));
103 } else {
104 return Filesystem::fileExists($this->getCachefilePath());
105 }
106 }
107
108 public function hasExpired($ttl = 86400, $cacheRevision = NULL) {
109 // If the cache for the parent is expired we do not need to check cache for the current object, because we only serve AJAX cache for pages already cached by NitroPack
110 if ($this->parent && $this->parent->hasExpired($ttl, $cacheRevision)) return true;
111
112 if ($this->useInvalidated) {
113 $this->convertToStaleCache();
114 $cachefilePath = $this->getCachefilePath("stale");
115 $mtime = Filesystem::fileMtime(dirname($cachefilePath));
116 } else {
117 $cachefilePath = $this->getCachefilePath();
118 $mtime = Filesystem::fileMtime($cachefilePath);
119 }
120
121 if (time() - $mtime >= $ttl) {
122 return true;
123 } else {
124 try {
125 $headers = Filesystem::fileGetHeaders($cachefilePath);
126 if (!empty($headers["x-nitro-expires"]) && time() > (int)$headers["x-nitro-expires"]) {
127 return true;
128 }
129
130 // Check if the revision has changed which makes cache file obsolete
131 if (!empty($headers["x-nitro-rev"]) && $cacheRevision && $cacheRevision != $headers["x-nitro-rev"]) {
132 return true;
133 }
134 } catch (\Exception $e) {
135 return true;
136 }
137 }
138 return false;
139 }
140
141 public function setContent($content, $headers = NULL) {
142 if (!$this->dataDir) return;
143
144 $filePath = $this->getCachefilePath();
145 if (Filesystem::createDir(dirname($filePath))) {
146 if (!Filesystem::filePutContents($filePath, $content, $headers)) {
147 return false;
148 } else {
149 return $this->compress($filePath);
150 }
151 }
152 }
153
154 private function headersFlatten($headers) {
155 // The headers from fileGetAll come as associative array
156 // They must be converted to an array of strings before being passed to filePutContents, which is what this function does
157 $headersFlat = array();
158 foreach ($headers as $name => $value) {
159 if (is_array($value)) {
160 foreach ($value as $subValue) {
161 $headersFlat[] = $name . ":" . $subValue;
162 }
163 } else {
164 $headersFlat[] = $name . ":" . $value;
165 }
166 }
167
168 return $headersFlat;
169 }
170
171 private function compress($filePath) {
172 $fileInfo = Filesystem::fileGetAll($filePath);
173 $fileInfo->headers["Content-Encoding"] = "gzip";
174 return Filesystem::filePutContents($filePath . ".gz", gzencode($fileInfo->content, 4), $this->headersFlatten($fileInfo->headers)); // save compressed version of the cache file
175 }
176
177 public function readfile() {
178 if ($this->useInvalidated) {
179 $this->convertToStaleCache();
180 $filePath = $this->getCachefilePath("stale");
181 } else {
182 $filePath = $this->getCachefilePath();
183 }
184
185 if ($this->canUseCompression() && (ini_get("zlib.output_compression") === "0" || ini_set("zlib.output_compression", "0") !== false)) {
186 $filePath .= ".gz";
187 }
188 $file = Filesystem::fileGetAll($filePath);
189 foreach ($file->headers as $name => $value) {
190 if (is_array($value)) {
191 foreach ($value as $subVal) {
192 header($name . ": " . $subVal, false);
193 }
194 } else {
195 header("$name: $value", false);
196 }
197 }
198
199 echo $file->content;
200 }
201
202 public function getFileContents() {
203 if ($this->useInvalidated) {
204 $this->convertToStaleCache();
205 return Filesystem::fileGetContents($this->getCachefilePath("stale"));
206 }
207 return Filesystem::fileGetContents($this->getCachefilePath());
208 }
209
210 private function convertToStaleCache() {
211 $staleFile = $this->getCachefilePath("stale");
212 if (!Filesystem::fileExists($staleFile)) {
213 $freshFile = $this->getCachefilePath();
214 if (Filesystem::fileExists($freshFile)) {
215 $fileInfo = Filesystem::fileGetAll($freshFile);
216 $newContent = str_replace("NITROPACK_STATE='FRESH'", "NITROPACK_STATE='STALE'", $fileInfo->content);
217 Filesystem::filePutContents($freshFile, $newContent, $this->headersFlatten($fileInfo->headers));
218 Filesystem::rename($freshFile, $staleFile);
219 Filesystem::deleteFile($freshFile . ".gz");
220 $this->compress($staleFile);
221 }
222 }
223 }
224
225 private function cookiePrefix() {
226 $prefix = '';
227
228 ksort($this->cookies);
229
230 foreach ($this->cookies as $cookieName=>$cookieValue) {
231 foreach ($this->supportedCookies as $cookie) {
232 if (preg_match('/' . NitroPack::wildcardToRegex($cookie) . '/', $cookieName)) {
233 $prefix .= $cookieName.'='.$cookieValue.';';
234 }
235 }
236 }
237
238 return substr(md5($prefix), 0, 16);
239 }
240
241 private function sslPrefix() {
242 return $this->url->getScheme() == "https" ? "ssl-" : "";
243 }
244
245 private function ajaxPrefix() {
246 return $this->isAjax && $this->parent ? "ajax-" . md5($this->url->getUrl()) . "-" : "";
247 }
248
249 private function customCachePrefix() {
250 $customCachePrefix = NitroPack::getCustomCachePrefix();
251 return $customCachePrefix ? $customCachePrefix . "-" : "";
252 }
253
254 private function isCompressionAllowed() {
255 return isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false;
256 }
257
258 private function canUseCompression() {
259 return $this->useCompression && $this->isCompressionAllowed() && !headers_sent();
260 }
261
262 public function nameOfCachefile() {
263 return $this->customCachePrefix() . $this->ajaxPrefix() . $this->sslPrefix() . $this->cookiePrefix() . ".html";
264 }
265
266 public function getCachefilePath($suffix = "") {
267 if ($suffix) $suffix = "." . $suffix;
268 if ($this->isAjax && $this->referer) {
269 return self::getUrlDir($this->dataDir, $this->referer->getUrl(), $this->useInvalidated) . "/" . $this->nameOfCachefile() . $suffix;
270 }
271 return self::getUrlDir($this->dataDir, $this->url->getUrl(), $this->useInvalidated) . "/" . $this->nameOfCachefile() . $suffix;
272 }
273 }
274