PluginProbe
RabbitLoader / 4.0.2
RabbitLoader v4.0.2
4.0.2 4.0.1 4.0 3.2.0 3.1.1 3.1.0 3.0.5 3.0.3 3.0.4 2.17.1 2.17.2 2.17.3 2.17.4 2.17.5 2.17.6 2.17.7 2.18.0 2.18.1 2.18.2 2.18.3 2.18.4 2.18.5 2.18.6 2.18.7 2.18.8 All 129 releases
rabbit-loader / inc / RabbitLoader / SDK / Cache.php

Cache.php in RabbitLoader 4.0.2, at inc/RabbitLoader/SDK/Cache.php

273 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace RabbitLoader\SDK;
4
5 class Cache
6 {
7
8 const TTL_LONG = "long";
9 const TTL_SHORT = "short";
10
11 private $request_url = '';
12 private $fp_long = '';
13 private $fp_short = '';
14 private $debug = false;
15 private $rootDir = '';
16 private $file;
17 private $rlCacheRebuildFlag = '"rlCacheRebuild": "Y"';
18
19 public function __construct($request_url, $rootDir)
20 {
21 $this->request_url = $request_url;
22 $this->rootDir = rtrim($rootDir, DIRECTORY_SEPARATOR); //sep may or may not exist
23 $this->rootDir = $this->rootDir . DIRECTORY_SEPARATOR; //ensure sep is always there
24 $this->file = new File();
25
26 $hash = md5($this->request_url);
27 $this->setPath($hash);
28 }
29
30 /**
31 * @param bool $debug
32 */
33 public function setDebug($debug)
34 {
35 $this->debug = $debug;
36 $this->file->setDebug($debug);
37 }
38
39 private function createDirs()
40 {
41 $rootDir = rtrim($this->rootDir, DIRECTORY_SEPARATOR);
42 if (!is_dir($rootDir)) {
43 mkdir($rootDir, 0775, true);
44 }
45
46 $rootDir = $rootDir . DIRECTORY_SEPARATOR;
47
48 if (!is_dir($rootDir . self::TTL_LONG)) {
49 if (!mkdir($rootDir . self::TTL_LONG, 0775, true)) {
50 error_log("rabbitloader failed to create cache directory inside " . $rootDir);
51 Util::sendHeader('x-rl-create-dirs: failed', true);
52 }
53 }
54 if (!is_dir($rootDir . self::TTL_SHORT)) {
55 if (!mkdir($rootDir . self::TTL_SHORT, 0775, true)) {
56 error_log("rabbitloader failed to create cache directory inside " . $rootDir);
57 Util::sendHeader('x-rl-create-dirs: failed', true);
58 } else {
59 //directory created successfully
60 $this->addHtaccess();
61 }
62 }
63 }
64
65 private function addHtaccess()
66 {
67 $loc = $this->rootDir . ".htaccess";
68 if (!file_exists($loc)) {
69 $content = "deny from all";
70 $this->file->fpc($loc, $content);
71 }
72
73 return file_exists($loc);
74 }
75
76 private function getPathForTTL($ttl, $fileType)
77 {
78 return $ttl === self::TTL_LONG ? $this->fp_long . '_' . $fileType : $this->fp_short . '_' . $fileType;
79 }
80
81 public function exists($ttl)
82 {
83 return file_exists($this->getPathForTTL($ttl, 'c'));
84 }
85
86 public function fresh($ttl, $ts)
87 {
88 $fp = $this->getPathForTTL($ttl, 'c');
89 if ($this->exists($ttl) && $ts && ($ts > 631152000)) {
90 $mt = filemtime($fp);
91 if ($this->debug) {
92 Util::sendHeader('x-rl-mtime: ' . $mt, true);
93 Util::sendHeader('x-rl-fresh: ' . $mt . '>' . $ts, true);
94 Util::sendHeader('x-rl-fpc: ' . $fp, false);
95 }
96 if ($mt && ($mt > $ts)) {
97 $content = file_get_contents($fp);
98 if ($content === false || str_contains($content, $this->rlCacheRebuildFlag)) {
99 return false;
100 }
101 return true;
102 }
103 }
104 return false;
105 }
106
107 public function delete($ttl)
108 {
109 $count = 0;
110 $fc = $this->getPathForTTL($ttl, 'c');
111 $fh = $this->getPathForTTL($ttl, 'h');
112
113 if (is_file($fc) && (($this->debug && unlink($fc)) || @unlink($fc))) {
114 $count++;
115 }
116 if (is_file($fh) && (($this->debug && unlink($fh)) || @unlink($fh))) {
117 $count++;
118 }
119 return $count;
120 }
121
122 public function collectGarbage($mtime)
123 {
124 $this->createDirs();
125 $lock = $this->rootDir . 'garbage.lock';
126 if (!$this->file->lockForTime($lock, $mtime)) {
127 return false;
128 }
129 $this->file->cleanDir($this->rootDir . self::TTL_LONG, 500, 30 * 24 * 3600);
130 $this->file->cleanDir($this->rootDir . self::TTL_SHORT, 500, 1800);
131 return true;
132 }
133
134 public function deleteAll()
135 {
136 $count = 0;
137 $count += $this->file->cleanDir($this->rootDir . self::TTL_LONG, 0, 0);
138 $count += $this->file->cleanDir($this->rootDir . self::TTL_SHORT, 0, 0);
139 return $count;
140 }
141
142 public function invalidate()
143 {
144 $fp = $this->getPathForTTL(self::TTL_LONG, 'c');
145 if (is_file($fp)) {
146 $content = file_get_contents($fp);
147 if ($content !== false) {
148 $rlModified = '"rlModified": "' . date('c') . '"';
149 $content = str_ireplace(['"rlCacheRebuild":"N"', '"rlCacheRebuild": "N"', '"rlModified":""', '"rlModified": ""'], [$this->rlCacheRebuildFlag, $this->rlCacheRebuildFlag, $rlModified, $rlModified], $content);
150 $this->file->fpc($fp, $content);
151 }
152 }
153 }
154
155
156 public function &get($ttl, $type)
157 {
158 $fp = $this->getPathForTTL($ttl, $type);
159 $content = '';
160 if (file_exists($fp)) {
161 $content = file_get_contents($fp);
162 }
163 return $content;
164 }
165
166 public function serve()
167 {
168 if ($this->exists(self::TTL_LONG)) {
169 $content = file_get_contents($this->getPathForTTL(self::TTL_LONG, 'c'));
170 if ($content !== false) {
171 if ($this->valid($content)) {
172 if (file_exists($this->getPathForTTL(self::TTL_LONG, 'h'))) {
173 //header is optional
174 $this->sendHeaders(file_get_contents($this->getPathForTTL(self::TTL_LONG, 'h')));
175 }
176 Util::sendHeader('x-rl-cache: hit', true);
177 echo $content;
178 return true;
179 }
180 }
181 }
182 return false;
183 }
184
185 public function save($ttl, &$content, &$headers)
186 {
187 $count = 0;
188 if (!$this->valid($content)) {
189 Util::sendHeader('x-rl-save: invalid-' . $ttl, true);
190 return $count;
191 }
192 $this->createDirs();
193 $headers = json_encode($headers, JSON_INVALID_UTF8_IGNORE);
194 if ($this->file->fpc($this->getPathForTTL($ttl, 'h'), $headers)) {
195 $count++;
196 }
197
198 if ($this->file->fpc($this->getPathForTTL($ttl, 'c'), $content)) {
199 $count++;
200 }
201 return $count;
202 }
203
204 private function valid(&$chunk)
205 {
206 if (empty($chunk)) {
207 return false;
208 }
209 if (stripos($chunk, '</html>') !== false || stripos($chunk, '</body>') !== false) {
210 return true;
211 }
212 return false;
213 }
214
215 private function sendHeaders($headers)
216 {
217 $headers_sent = 0;
218 if (empty($headers)) {
219 return $headers_sent;
220 }
221 if (!is_array($headers)) {
222 $headers_decoded = json_decode($headers, true);
223 if ($headers_decoded === false) {
224 $e = new \Error(json_last_error_msg());
225 Exc:: catch($e, $headers);
226 } else {
227 $headers = $headers_decoded;
228 }
229 }
230 if (!empty($headers)) {
231 foreach ($headers as $key => $values) {
232 foreach ($values as $val) {
233 header($key . ':' . $val, false);
234 $headers_sent++;
235 }
236 }
237 }
238 return $headers_sent;
239 }
240
241 public function setPath($hash)
242 {
243 $this->fp_long = $this->rootDir . self::TTL_LONG . DIRECTORY_SEPARATOR . $hash;
244 $this->fp_short = $this->rootDir . self::TTL_SHORT . DIRECTORY_SEPARATOR . $hash;
245 }
246
247 public function setVariant($variant)
248 {
249 if (empty($variant) || !is_array($variant)) {
250 return;
251 }
252 ksort($variant);
253 $hash = md5($this->request_url . json_encode($variant));
254 $this->setPath($hash);
255 }
256
257 public function getCacheCount()
258 {
259 return $this->file->countFiles($this->rootDir . self::TTL_LONG);
260 }
261
262 public function set429()
263 {
264 $lock = $this->rootDir . '429.lock';
265 return $this->file->lock($lock);
266 }
267 public function get429()
268 {
269 $lock = $this->rootDir . '429.lock';
270 return $this->file->isLocked($lock, strtotime('-15 minutes'));
271 }
272 }
273