PluginProbe
Docket Cache – Object Cache Accelerator / trunk
Docket Cache – Object Cache Accelerator vtrunk
26.04.05 trunk 22.07.01 22.07.02 22.07.03 22.07.04 22.07.05 23.08.01 23.08.02 24.07.01 24.07.02 24.07.03 24.07.04 24.07.05 24.07.06 24.07.07 26.04.03 26.04.04
docket-cache / includes / src / Becache.php

Becache.php in Docket Cache – Object Cache Accelerator trunk, at includes/src/Becache.php

421 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Docket Cache.
4 *
5 * @author Nawawi Jamili
6 * @license MIT
7 *
8 * @see https://github.com/nawawi/docket-cache
9 */
10
11 /*
12 * This class is a minimal version of cache.php, only used for object-cache.php.
13 *
14 * Reference:
15 * wp-content/plugins/docket-cache/includes/cache.php
16 * wp-content/plugins/docket-cache/includes/object-cache.php
17 */
18
19 namespace Nawawi\DocketCache;
20
21 \defined('ABSPATH') || exit;
22
23 final class Becache
24 {
25 private $cache_path;
26 private $cache_maxsize = 3145728;
27 private $cache_maxttl = 345600;
28 private $multisite = false;
29 private $blog_prefix;
30 private $qlimit = 1000;
31 private static $inst;
32 private $network_id = 1;
33 private $max_execution_time = 0;
34
35 public function __construct()
36 {
37 $this->multisite = \function_exists('is_multisite') && is_multisite();
38 $this->blog_prefix = $this->switch_to_blog(get_current_blog_id());
39
40 $this->cache_path = $this->fs()->define_cache_path($this->cf()->dcvalue('PATH'));
41 if (\function_exists('is_multisite') && is_multisite()) {
42 $this->cache_path = nwdcx_network_dirpath($this->cache_path);
43 }
44
45 if ($this->cf()->is_dcint('MAXSIZE', $dcvalue)) {
46 if (!empty($dcvalue)) {
47 $this->cache_maxsize = $this->fs()->sanitize_maxsize($dcvalue);
48 }
49 }
50
51 if ($this->cf()->is_dcint('MAXTTL', $dcvalue)) {
52 if (!empty($dcvalue)) {
53 $this->cache_maxttl = $this->fs()->sanitize_maxttl($dcvalue);
54 }
55 }
56
57 $this->network_id = (int) nwdcx_network_id();
58 $this->max_execution_time = $this->fs()->get_max_execution_time();
59 }
60
61 public static function export()
62 {
63 if (!isset(self::$inst)) {
64 self::$inst = new self();
65 }
66
67 self::$inst->export_transient();
68 self::$inst->export_alloptions();
69 }
70
71 private function fs()
72 {
73 static $inst;
74 if (!\is_object($inst)) {
75 $inst = new Filesystem();
76 }
77
78 return $inst;
79 }
80
81 private function cf()
82 {
83 static $inst;
84 if (!\is_object($inst)) {
85 $inst = new Constans();
86 }
87
88 return $inst;
89 }
90
91 private function cache_key($key, $group)
92 {
93 if ($this->multisite && 'site-transient' === $group) {
94 $key = $this->blog_prefix.$key;
95 }
96
97 return $key;
98 }
99
100 private function item_hash($str, $length = 12)
101 {
102 if (empty($length)) {
103 return md5($str);
104 }
105
106 return substr(md5($str), 0, $length);
107 }
108
109 private function get_file_path($key, $group)
110 {
111 $hash_group = $this->item_hash($group);
112 $hash_key = $this->item_hash($key);
113
114 $index = $hash_group.'-'.$hash_key;
115
116 if ($this->cf()->is_dcfalse('CHUNKCACHEDIR', true)) {
117 return $this->cache_path.$index.'.php';
118 }
119
120 $chunk_path = $this->fs()->get_chunk_path($hash_group, $hash_key);
121
122 return $this->cache_path.$chunk_path.$index.'.php';
123 }
124
125 private function dump_code($file, $arr)
126 {
127 $data = $this->fs()->export_var($arr, $error);
128 if (false === $data) {
129 return false;
130 }
131
132 $code = $this->fs()->code_stub($data);
133 $stat = $this->fs()->dump($file, $code, false);
134
135 if (false === $stat) {
136 return false;
137 }
138
139 if (-1 === $stat) {
140 return false;
141 }
142
143 $this->fs()->validate_fatal_error_file($file);
144
145 return $stat;
146 }
147
148 private function store_cache($key, $data, $group, $expire = 0)
149 {
150 if (!$this->fs()->mkdir_p($this->cache_path)) {
151 return false;
152 }
153
154 $cache_key = $this->cache_key($key, $group);
155 $file = $this->get_file_path($cache_key, $group);
156
157 // chunk dir
158 if ($this->cf()->is_dctrue('CHUNKCACHEDIR', true) && !$this->fs()->mkdir_p(\dirname($file))) {
159 return false;
160 }
161
162 if ($this->fs()->is_transient($group)) {
163 // transient timeout already as timestamp in DB.
164 $timeout = $expire > 0 ? $expire : time() + 3600;
165 } elseif ('options' === $group) {
166 // initial set to 1 hour.
167 $timeout = time() + 3600;
168 }
169
170 $timeout = $timeout > $this->cache_maxttl ? time() + $this->cache_maxttl : $timeout;
171 $type = \gettype($data);
172 if ('NULL' === $type && null === $data) {
173 $data = '';
174 }
175
176 if (!empty($data)) {
177 $len = 0;
178 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
179 if (\function_exists('maybe_serialize')) {
180 $len = \strlen(@maybe_serialize($data));
181 } else {
182 $len = \strlen(@serialize($data));
183 }
184 nwdcx_suppresserrors($nwdcx_suppresserrors);
185
186 if ($len >= $this->cache_maxsize) {
187 $this->fs()->unlink($file, false);
188
189 return false;
190 }
191
192 if ('string' === $type) {
193 $data = nwdcx_unserialize($data);
194 } elseif ('array' === $type) {
195 $data_r = nwdcx_arraymap('nwdcx_unserialize', $data);
196
197 if (!empty($data_r)) {
198 $data = $data_r;
199 }
200 unset($data_r);
201 }
202 }
203
204 $meta = [];
205 $meta['timestamp'] = time();
206
207 if ($this->multisite) {
208 $meta['network_id'] = $this->network_id;
209 }
210
211 $final_type = \gettype($data);
212 if ('string' === $final_type && nwdcx_serialized($data)) {
213 $final_type = 'string_serialize';
214 } elseif ('array' === $final_type) {
215 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
216 $export_data = @var_export($data, 1);
217 if (!empty($export_data)) {
218 if (false !== strpos($export_data, 'Requests_Utility_CaseInsensitiveDictionary::__set_state')) {
219 $data = @serialize($data);
220 if (nwdcx_serialized($data)) {
221 $final_type = 'array_serialize';
222 }
223 }
224
225 if ('array' === $final_type && $this->fs()->is_transient($group) && false !== strpos($export_data, '::__set_state')) {
226 $data = @serialize($data);
227 if (nwdcx_serialized($data)) {
228 $final_type = 'array_serialize';
229 }
230 }
231 }
232 unset($export_data);
233 nwdcx_suppresserrors($nwdcx_suppresserrors);
234 }
235
236 $meta['site_id'] = get_current_blog_id();
237 $meta['group'] = $group;
238 $meta['key'] = $cache_key;
239 $meta['type'] = $final_type;
240 $meta['timeout'] = $timeout;
241 $meta['data'] = $data;
242
243 if (true === $this->dump_code($file, $meta)) {
244 return true;
245 }
246
247 return false;
248 }
249
250 public function export_transient()
251 {
252 if ($this->cf()->is_dctrue('TRANSIENTDB')) {
253 return false;
254 }
255
256 if (!nwdcx_wpdb($wpdb)) {
257 return false;
258 }
259
260 $suppress = $wpdb->suppress_errors(true);
261 $collect = [];
262
263 // $results = $wpdb->get_results('SELECT `option_id`,`option_name`,`option_value` FROM `'.$wpdb->options.'` WHERE `option_name` LIKE "_transient_%" OR `option_name` LIKE "_site_transient_%" ORDER BY `option_id` ASC LIMIT '.$this->qlimit, ARRAY_A);
264 $results = $wpdb->get_results('SELECT `option_id`,`option_name`,`option_value` FROM `'.$wpdb->options.'` WHERE `option_name` RLIKE "^(_site)?(_transient)(_timeout)?_.*?" ORDER BY `option_id` ASC LIMIT '.$this->qlimit, ARRAY_A);
265 if (!empty($results) && \is_array($results)) {
266 while ($row = @array_shift($results)) {
267 if ($this->max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $this->max_execution_time) {
268 $collect = [];
269 break;
270 }
271
272 // ignore empty value
273 if ('' === $row['option_value']) {
274 continue;
275 }
276
277 $key = @preg_replace('@^(_site)?(_transient)(_timeout)?_@', '', $row['option_name']);
278 if (!isset($collect[$key])) {
279 $collect[$key] = ['value' => '', 'group' => 'transient', 'timeout' => 0];
280 }
281
282 $collect[$key]['value'] = $row['option_value'];
283 if ('_site_' === substr($row['option_name'], 0, 6)) {
284 $collect[$key]['group'] = 'site-transient';
285 }
286
287 if (false !== strpos($row['option_name'], '_transient_timeout_')) {
288 $collect[$key]['timeout'] = (int) $row['option_value'];
289
290 if (time() > $collect[$key]['timeout']) {
291 unset($collect[$key]);
292 }
293 }
294 }
295
296 if (!empty($collect)) {
297 foreach ($collect as $key => $arr) {
298 if ($this->max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $this->max_execution_time) {
299 break;
300 }
301
302 $timeout = isset($arr['timeout']) ? $arr['timeout'] : 0;
303 $this->store_cache($key, $arr['value'], $arr['group'], $timeout);
304 }
305 }
306 }
307
308 $collect = [];
309
310 if ($this->multisite && isset($wpdb->sitemeta)) {
311 // $results = $wpdb->get_results('SELECT `meta_id`,`meta_key`,`meta_value` FROM `'.$wpdb->sitemeta.'` WHERE `meta_key` LIKE "_site_transient_%" ORDER BY `meta_id` ASC LIMIT '.$this->qlimit, ARRAY_A);
312 $results = $wpdb->get_results('SELECT `meta_id`,`meta_key`,`meta_value` FROM `'.$wpdb->sitemeta.'` WHERE `meta_key` RLIKE "^(_site_transient)(_timeout)?_.*?" ORDER BY `meta_id` ASC LIMIT '.$this->qlimit, ARRAY_A);
313 if (!empty($results) && \is_array($results)) {
314 while ($row = @array_shift($results)) {
315 if ($this->max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $this->max_execution_time) {
316 $collect = [];
317 break;
318 }
319
320 // ignore empty value
321 if ('' === $row['meta_value']) {
322 continue;
323 }
324
325 $key = @preg_replace('@^(_site_transient)(_timeout)?_@', '', $row['meta_key']);
326 if (!isset($collect[$key])) {
327 $collect[$key] = ['value' => '', 'group' => 'site-transient', 'timeout' => 0];
328 }
329
330 $collect[$key]['value'] = $row['meta_value'];
331
332 if (false !== strpos($row['meta_key'], '_site_transient_timeout_')) {
333 $collect[$key]['timeout'] = (int) $row['meta_value'];
334
335 if (time() > $collect[$key]['timeout']) {
336 unset($collect[$key]);
337 }
338 }
339 }
340
341 if (!empty($collect)) {
342 foreach ($collect as $key => $arr) {
343 $timeout = isset($arr['timeout']) ? $arr['timeout'] : 0;
344 $this->store_cache($key, $arr['value'], $arr['group'], $timeout);
345 }
346 }
347 }
348 }
349
350 unset($collect, $results);
351 $wpdb->suppress_errors($suppress);
352
353 return true;
354 }
355
356 public static function cleanup_transient()
357 {
358 if (nwdcx_construe('TRANSIENTDB')) {
359 return false;
360 }
361
362 if (\function_exists('nwdcx_cleanuptransient')) {
363 return nwdcx_cleanuptransient();
364 }
365
366 return false;
367 }
368
369 public function export_alloptions()
370 {
371 if (!nwdcx_wpdb($wpdb) || $this->multisite) {
372 return false;
373 }
374
375 // Only export if autoload=yes and not transient.
376 $suppress = $wpdb->suppress_errors(true);
377 $alloptions_db = $wpdb->get_results('SELECT `option_name`,`option_value` FROM `'.$wpdb->options.'` WHERE autoload = \'yes\' AND `option_name` NOT LIKE "_transient_%" AND `option_name` NOT LIKE "_site_transient_%"', ARRAY_A);
378 if (empty($alloptions_db)) {
379 return false;
380 }
381 $wpdb->suppress_errors($suppress);
382
383 $alloptions = [];
384
385 if (\is_array($alloptions_db)) {
386 $is_filter = $this->cf()->is_dctrue('WPOPTALOAD', true);
387 $wp_options = [];
388 if ($is_filter) {
389 $wp_options = $this->fs()->keys_alloptions();
390 }
391 foreach ($alloptions_db as $num => $options) {
392 if ($this->max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $this->max_execution_time) {
393 $alloptions = [];
394 break;
395 }
396
397 $key = $options['option_name'];
398 if ($is_filter && \array_key_exists($key, $wp_options)) {
399 continue;
400 }
401 $alloptions[$key] = $options['option_value'];
402 }
403
404 if (!empty($alloptions)) {
405 $this->store_cache('alloptions', $alloptions, 'options');
406 }
407 }
408
409 unset($alloptions, $alloptions_db);
410
411 // always true
412 return true;
413 }
414
415 private function switch_to_blog($blog_id)
416 {
417 $blog_id = (int) $blog_id;
418 $this->blog_prefix = $this->multisite ? $blog_id.':' : '';
419 }
420 }
421