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 / Filesystem.php

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

1,704 lines 46.3 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 namespace Nawawi\DocketCache;
12
13 \defined('ABSPATH') || exit;
14
15 use Nawawi\DocketCache\Exporter\VarExporter;
16
17 class Filesystem
18 {
19 /**
20 * is_docketcachedir.
21 */
22 public function is_docketcachedir($path)
23 {
24 static $cached = [];
25
26 if (!empty($cached[$path])) {
27 return true;
28 }
29
30 $name = 'docket-cache';
31 $ok = false;
32
33 $dir = nwdcx_normalizepath($path);
34
35 if (false === strpos($dir.'/', '/'.$name.'/')) {
36 return $ok;
37 }
38
39 $dir = array_reverse(explode('/', trim($dir, '/')));
40
41 // 28042022: new cache directory.
42 // depth = 3: cache/docket-cache/93/b2/8a/
43 // depth = 4: cache/docket-cache/network-1/93/b2/8a/
44 // depth = 5: cache/docket-cache/network/network-1/93/b2/8a/
45 $maxdepth = 5;
46 foreach ($dir as $n => $c) {
47 if ($n <= $maxdepth && 0 === strcmp($name, $c)) {
48 $ok = true;
49 $cached[$path] = 1;
50 break;
51 }
52 }
53
54 return $ok;
55 }
56
57 /**
58 * is_docketcachefile.
59 */
60 public function is_docketcachefile($file)
61 {
62 static $cached = [];
63
64 if (!empty($cached[$file])) {
65 return true;
66 }
67
68 if (false !== strpos($file, '/docket-cache/') && @preg_match('@^([a-z0-9]{12})\-([a-z0-9]{12})\.php$@', basename($file))) {
69 $cached[$file] = 1;
70
71 return true;
72 }
73
74 return false;
75 }
76
77 /**
78 * is_docketcachegroup.
79 */
80 public function is_docketcachegroup($group, $key = '')
81 {
82 return 'docketcache' === substr($group, 0, 11) || !empty($key) && 'docketcache' === substr($key, 0, 11);
83 }
84
85 /**
86 * is_transient.
87 */
88 public function is_transient($group)
89 {
90 if (\is_array($group)) {
91 return \in_array($group, ['transient', 'site-transient']);
92 }
93
94 return 'transient' === $group || 'site-transient' === $group;
95 }
96
97 /**
98 * is_wp_options.
99 */
100 public function is_wp_options($group)
101 {
102 if (\is_array($group)) {
103 return \in_array($group, ['options', 'site-options']);
104 }
105
106 return 'options' === $group || 'site-options' === $group;
107 }
108
109 /**
110 * is_wp_cache_group_queries.
111 */
112 public function is_wp_cache_group_queries($group)
113 {
114 return \in_array($group, ['term-queries', 'post-queries', 'comment-queries', 'site-queries', 'network-queries', 'user-queries']);
115 }
116
117 /**
118 * is_dirempty.
119 */
120 public function is_dirempty($dir)
121 {
122 foreach (new \DirectoryIterator($dir) as $object) {
123 if ($object->isDot()) {
124 continue;
125 }
126
127 return false;
128 }
129
130 return true;
131 }
132
133 /**
134 * get_max_execution_time.
135 */
136 public function get_max_execution_time($second = 0)
137 {
138 $max_execution_time = (int) \ini_get('max_execution_time');
139
140 $second = (int) $second;
141 if ($second > 0 && $max_execution_time > 0 && $second > $max_execution_time) {
142 set_time_limit($second);
143 $max_execution_time = (int) \ini_get('max_execution_time');
144 }
145
146 if ($max_execution_time > 10) {
147 --$max_execution_time;
148 }
149
150 return $max_execution_time;
151 }
152
153 /**
154 * filesize.
155 */
156 public function filesize($file)
157 {
158 if (!@is_file($file)) {
159 return 0;
160 }
161
162 return sprintf('%u', @filesize($file));
163 }
164
165 /**
166 * touch.
167 */
168 public function touch($file, $time = 0, $atime = 0)
169 {
170 if (0 == $time) {
171 $time = time();
172 }
173
174 if (0 == $atime) {
175 $atime = time();
176 }
177
178 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
179
180 $ok = @touch($file, $time, $atime);
181
182 // user:group not same -> Utime failed: Operation not permitted
183 if (!$ok) {
184 $e = error_get_last();
185 if (!\is_array($e)) {
186 nwdcx_throwable(__METHOD__, $e);
187 }
188 }
189
190 // restore error level
191 nwdcx_suppresserrors($nwdcx_suppresserrors);
192
193 return $ok;
194 }
195
196 /**
197 * getchmod.
198 */
199 public function getchmod($file)
200 {
201 return substr(decoct(@fileperms($file)), -3);
202 }
203
204 /**
205 * chmod.
206 */
207 public function chmod($file, $mode = false)
208 {
209 if (!$mode) {
210 if (@is_file($file) && \defined('FS_CHMOD_FILE')) {
211 $mode = FS_CHMOD_FILE;
212 } elseif (@is_dir($file) && \defined('FS_CHMOD_DIR')) {
213 $mode = FS_CHMOD_DIR;
214 } else {
215 clearstatcache();
216 $stat = @stat(\dirname($file));
217 $mode = $stat['mode'] & 0007777;
218
219 if (@is_file($file)) {
220 $mode = $mode & 0000666;
221 }
222 }
223 }
224
225 clearstatcache();
226
227 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
228 $ok = false;
229 try {
230 if (@is_file($file)) {
231 $ok = @chmod($file, $mode);
232 }
233 } catch (\Throwable $e) {
234 nwdcx_throwable(__METHOD__, $e);
235 }
236
237 nwdcx_suppresserrors($nwdcx_suppresserrors);
238
239 return $ok;
240 }
241
242 /**
243 * mkdir.
244 */
245 public function mkdir_p($path)
246 {
247 $parent = \dirname($path);
248 $okperms = [
249 '777',
250 '775',
251 '755',
252 ];
253
254 if (@is_dir($path) && \in_array($this->getchmod($path), $okperms) && \in_array($this->getchmod($parent), $okperms)) {
255 return true;
256 }
257
258 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
259
260 if (\function_exists('wp_mkdir_p')) {
261 $ok = @wp_mkdir_p($path);
262 } else {
263 $stat = @stat($parent);
264 if ($stat) {
265 $dir_perms = $stat['mode'] & 0007777;
266 } else {
267 $dir_perms = 0777;
268 }
269
270 $ok = @mkdir($path, $dir_perms, true);
271 }
272
273 nwdcx_suppresserrors($nwdcx_suppresserrors);
274
275 if (!$ok) {
276 return false;
277 }
278
279 if (!\in_array($this->getchmod($parent), $okperms)) {
280 $this->chmod($parent, 0755);
281 }
282
283 if (!\in_array($this->getchmod($path), $okperms)) {
284 $this->chmod($path, 0755);
285 }
286
287 return true;
288 }
289
290 /**
291 * copy.
292 */
293 public function copy($src, $dst)
294 {
295 $this->opcache_flush($src);
296 $this->opcache_flush($dst);
297
298 if (@copy($src, $dst)) {
299 $this->chmod($dst);
300
301 return true;
302 }
303
304 return false;
305 }
306
307 /**
308 * scanfiles.
309 */
310 public function scanfiles($dir, $maxdepth = null, $pattern = false)
311 {
312 $dir = nwdcx_normalizepath(realpath($dir));
313 if (false === $dir || !is_dir($dir) || !is_readable($dir)) {
314 return [];
315 }
316
317 if (null === $maxdepth) {
318 $maxdepth = 13;
319 }
320
321 if (empty($pattern)) {
322 $pattern = '@^(dump_)?([a-z0-9_]+)\-([a-z0-9]+).*\.php$@';
323 }
324
325 $rec_dir_iterator_flags = \FilesystemIterator::SKIP_DOTS | \RecursiveDirectoryIterator::KEY_AS_FILENAME | \RecursiveDirectoryIterator::CURRENT_AS_FILEINFO;
326 $rec_dir_iterator = new \RecursiveDirectoryIterator($dir, $rec_dir_iterator_flags);
327 $reg_ite_iterator = new \RecursiveIteratorIterator($rec_dir_iterator);
328 $reg_ite_pattern = $pattern;
329 $reg_ite_mode = \RegexIterator::MATCH;
330 $reg_ite_flags = \RegexIterator::USE_KEY;
331 $reg_iterator = new \RegexIterator($reg_ite_iterator, $reg_ite_pattern, $reg_ite_mode, $reg_ite_flags);
332 $reg_iterator->setMaxDepth($maxdepth);
333
334 return $reg_iterator;
335 }
336
337 /**
338 * validate_file.
339 */
340 public function validate_file($filename)
341 {
342 try {
343 $fileo = new \SplFileObject($filename, 'rb');
344 } catch (\Throwable $e) {
345 nwdcx_throwable(__METHOD__, $e);
346
347 return false;
348 }
349
350 if ($fileo->flock(\LOCK_EX)) {
351 $fileo->seek(\PHP_INT_MAX);
352 $lines = $fileo->key();
353 $object = new \LimitIterator($fileo, $lines - 2);
354 foreach ($object as $line) {
355 if (false !== strpos($line, '/*@DOCKET_CACHE_EOF*/')) {
356 $fileo->flock(\LOCK_UN);
357
358 return true;
359 }
360 }
361 $fileo->flock(\LOCK_UN);
362 }
363
364 $fileo = null;
365
366 return false;
367 }
368
369 /**
370 * export_var.
371 */
372 public function export_var($data, &$error = '')
373 {
374 // 28022023, self-note to future me.
375 // We use native var_export to improve cache writing, since VarExporter::export will use preg_replace_callback on string types and
376 // ReflectionClass to resolve class instances. Cached data may look not pretty.
377 try {
378 if (version_compare(\PHP_VERSION, '7.3.0', '>=') && !empty($data['type']) && \in_array($data['type'], ['object', 'array', 'string', 'string_serialize', 'array_serialize', 'integer', 'boolean'])) {
379 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
380 $arr_data = @var_export($data, true);
381 nwdcx_suppresserrors($nwdcx_suppresserrors);
382 // Return exported data, if doesn't have a class instance.
383 if (!empty($arr_data) && false === strpos($arr_data, '::__set_state')) {
384 return $arr_data;
385 }
386 }
387 } catch (\Throwable $e) {
388 nwdcx_throwable(__METHOD__, $e);
389 }
390
391 // If native var_export failed or not from cache.
392 try {
393 $data = VarExporter::export($data);
394 } catch (\Throwable $e) {
395 nwdcx_throwable(__METHOD__, $e);
396 $error = $e->getMessage();
397
398 // php < 7.3
399 if (false !== strpos($error, 'Cannot export value of type "stdClass"')) {
400 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
401 $data = @var_export($data, true);
402 $data = str_replace('stdClass::__set_state', '(object)', $data);
403 nwdcx_suppresserrors($nwdcx_suppresserrors);
404 } else {
405 $this->log('err', '000000000000-000000000000', 'export_var: '.$error);
406
407 return false;
408 }
409 }
410
411 // alias: shorter name
412 // map it in includes/compat.php
413 $data = str_replace(
414 '\Nawawi\Symfony\Component\VarExporter\Internal\\',
415 '\Nawawi\DocketCache\Exporter\\',
416 $data
417 );
418
419 return $data;
420 }
421
422 /**
423 * shutdown_cleanup.
424 */
425 public function shutdown_cleanup($file, $seq = 10)
426 {
427 // for dump().
428 if (empty($this->filesize($file))) {
429 return false;
430 }
431
432 // dont use register_shutdown_function to avoid issue with page cache plugin
433 add_action(
434 'shutdown',
435 function () use ($file) {
436 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
437 clearstatcache(true, $file);
438 if (@is_file($file)) {
439 @unlink($file);
440 }
441 nwdcx_suppresserrors($nwdcx_suppresserrors);
442 },
443 $seq
444 );
445
446 return true;
447 }
448
449 /**
450 * unlink.
451 */
452 public function unlink($file, $is_delete = false, $is_block = false)
453 {
454 clearstatcache(true, $file);
455
456 // skip if not exist
457 if (!@is_file($file)) {
458 return true;
459 }
460
461 $ok = false;
462
463 $handle = @fopen($file, 'cb');
464 if ($handle) {
465 $lock = $is_block ? \LOCK_EX : \LOCK_EX | \LOCK_NB;
466 if (@flock($handle, $lock)) {
467 $ok = @ftruncate($handle, 0); // true, false
468 @flock($handle, \LOCK_UN);
469 }
470 @fclose($handle);
471 }
472
473 // bcoz we empty the file
474 $this->opcache_flush($file);
475
476 // Notify web-server OPcache when running under WP-CLI.
477 if (nwdcx_construe('WPCLI') && nwdcx_construe('WPCLI_OPCACHE') && $this->is_php($file)) {
478 CliOpcache::notify([$file]);
479 }
480
481 $do_delete = (nwdcx_construe('FLUSH_DELETE') && $this->is_php($file)) || $is_delete;
482
483 if ($do_delete && @unlink($file)) {
484 $ok = true;
485 }
486
487 // cleanup if ftruncate() failed
488 if (false === $ok) {
489 clearstatcache(true, $file);
490 if (@is_file($file) && !@unlink($file)) {
491 // try cleanup at shutdown
492 $this->shutdown_cleanup($file);
493 }
494 }
495
496 // reset all
497 clearstatcache(true);
498
499 // always true
500 return true;
501 }
502
503 /**
504 * put.
505 */
506 public function put($file, $data, $flag = 'cb', $is_block = false)
507 {
508 if (!$handle = @fopen($file, $flag)) {
509 return false;
510 }
511
512 $lock = $is_block ? \LOCK_EX : \LOCK_EX | \LOCK_NB;
513 $ok = false;
514 if (@flock($handle, $lock)) {
515 $len = \strlen($data);
516 $cnt = @fwrite($handle, $data);
517 @fflush($handle);
518 @flock($handle, \LOCK_UN);
519 if ($len === $cnt) {
520 $ok = true;
521 }
522 }
523 @fclose($handle);
524
525 if (false === $ok) {
526 $this->unlink($file, true);
527
528 return -1;
529 }
530
531 clearstatcache(true);
532
533 $this->opcache_flush($file);
534 $this->chmod($file);
535
536 return $ok;
537 }
538
539 /**
540 * dump.
541 */
542 public function dump($file, $data, $is_validate = false)
543 {
544 $dir = \dirname($file);
545 $tmpfile = $dir.'/'.'dump_'.uniqid().'_'.basename($file);
546
547 // cleanup at shutdown
548 $this->shutdown_cleanup($tmpfile, \PHP_INT_MAX);
549
550 // truncate reason
551 $this->opcache_flush($file);
552
553 // make query-monitor happy.
554 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
555
556 $ok = $this->put($tmpfile, $data, 'cb', true);
557 if (true === $ok) {
558 try {
559 clearstatcache(true, $tmpfile);
560 if (@is_file($tmpfile) && @rename($tmpfile, $file)) {
561 if (isset($nwdcx_suppresserrors)) {
562 nwdcx_suppresserrors($nwdcx_suppresserrors);
563 }
564
565 if ($is_validate && !$this->validate_file($file)) {
566 return false;
567 }
568
569 $this->chmod($file);
570
571 // compile
572 $this->opcache_compile($file);
573
574 return true;
575 }
576 } catch (\Throwable $e) {
577 nwdcx_throwable(__METHOD__, $e);
578 }
579
580 // failed to replace
581 $ok = false;
582 }
583
584 // cleanup if not bool true
585 clearstatcache(true, $tmpfile);
586 if (@is_file($tmpfile)) {
587 @unlink($tmpfile);
588 }
589
590 nwdcx_suppresserrors($nwdcx_suppresserrors);
591
592 clearstatcache(true);
593
594 // maybe -1, >= 1, false: return from put()
595 return $ok;
596 }
597
598 /**
599 * placeholder.
600 */
601 public function placeholder($path)
602 {
603 if (!@is_dir($path)) {
604 return false;
605 }
606
607 $file = rtrim($path, '/\\').'/index.html';
608 if (@is_file($file)) {
609 return false;
610 }
611
612 $code = '<html><head><meta name="robots" content="noindex, nofollow"><title>Docket Cache</title></head>';
613 $code .= '<body>Generated by <a href="https://wordpress.org/plugins/docket-cache/" rel="nofollow">Docket Cache</a></body></html>';
614
615 return $this->put($file, $code);
616 }
617
618 /**
619 * is_php.
620 */
621 public function is_php($file)
622 {
623 $file = basename($file);
624
625 return '.php' === substr($file, -4);
626 }
627
628 public function is_function_disabled($name)
629 {
630 $disable_functions = @\ini_get('disable_functions');
631 if (!empty($disable_functions)) {
632 $funcs = explode(',', $disable_functions);
633 if (\in_array($name, $funcs)) {
634 return true;
635 }
636 }
637
638 return false;
639 }
640
641 public function get_chunk_path($group, $key)
642 {
643 $chunk = substr($group, 0, 2).substr($key, 0, 2).substr($key, -2);
644
645 return chunk_split($chunk, 2, '/');
646 }
647
648 /**
649 * remove cache file with previous structure.
650 */
651 public function remove_non_chunk_cache($cache_path, $file)
652 {
653 if (nwdcx_construe('CHUNKCACHEDIR')) {
654 $filename = basename($file);
655 $filepath = rtrim($cache_path, '\\/').'/'.$filename;
656 if (is_file($filepath) && is_writable($filepath) && $this->is_docketcachefile($filepath)) {
657 return @unlink($filepath);
658 }
659 }
660
661 return false;
662 }
663
664 /**
665 * opcache_function_exists.
666 */
667 public function opcache_function_exists($name = null)
668 {
669 $list = [
670 'opcache_get_status',
671 'opcache_reset',
672 'opcache_compile_file',
673 'opcache_invalidate',
674 'opcache_is_script_cached',
675 'opcache_get_configuration',
676 ];
677
678 if (!empty($name)) {
679 return \in_array($name, $list) && !$this->is_function_disabled($name) && \function_exists($name);
680 }
681
682 foreach ($list as $func) {
683 if (!$this->is_function_disabled($name) && \function_exists($func)) {
684 return true;
685 }
686 }
687
688 return false;
689 }
690
691 /**
692 * is_opcache_enable.
693 */
694 public function is_opcache_enable()
695 {
696 if (@\ini_get('opcache.enable')) {
697 if (\function_exists('extension_loaded') && \extension_loaded('Zend OPcache')) {
698 return true;
699 }
700
701 if (\function_exists('get_loaded_extensions')) {
702 $arr = get_loaded_extensions(true);
703 if (\is_array($arr) && \in_array('Zend OPcache', $arr)) {
704 return true;
705 }
706 }
707
708 if ($this->opcache_function_exists()) {
709 return true;
710 }
711 }
712
713 return false;
714 }
715
716 /**
717 * is_opcache_blacklisted.
718 */
719 public function is_opcache_blacklisted()
720 {
721 $conf = \ini_get('opcache.blacklist_filename');
722 if (empty($conf)) {
723 return false;
724 }
725
726 $files = glob($conf);
727 if (empty($files) || !\is_array($files)) {
728 return false;
729 }
730
731 $abspath = rtrim(ABSPATH, '\\/');
732 foreach ($files as $file) {
733 if (!is_file($file) || !is_readable($file)) {
734 continue;
735 }
736 $buff = file($file, \FILE_SKIP_EMPTY_LINES | \FILE_IGNORE_NEW_LINES);
737 if (!empty($buff) && \is_array($buff)) {
738 foreach ($buff as $line) {
739 if (empty($line) || ';' === substr($line, 0, 1)) {
740 continue;
741 }
742
743 if ($abspath === substr(rtrim($line, '\\/'), 0, \strlen($abspath))) {
744 return true;
745 }
746 }
747 }
748 unset($buff);
749 }
750
751 return false;
752 }
753
754 /**
755 * is_opcache_filecache_only.
756 */
757 public function is_opcache_filecache_only()
758 {
759 return @\ini_get('opcache.file_cache_only');
760 }
761
762 /**
763 * opcache_filecache_only.
764 */
765 public function opcache_filecache_only()
766 {
767 if ($this->is_opcache_filecache_only() && \function_exists('opcache_get_status')) {
768 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
769 $data = opcache_get_status();
770 nwdcx_suppresserrors($nwdcx_suppresserrors);
771 if (!empty($data) && \is_array($data) && !empty($data['file_cache_only']) && !empty($data['file_cache']) && is_dir($data['file_cache'])) {
772 return $data;
773 }
774 }
775
776 return false;
777 }
778
779 /**
780 * opcache_filecache_scanfiles.
781 */
782 public function opcache_filecache_scanfiles($dir)
783 {
784 return $this->scanfiles($dir, -1, '@.*\.php\.bin$@');
785 }
786
787 /**
788 * opcache_filecache_flush.
789 */
790 public function opcache_filecache_flush($file)
791 {
792 if (!($fcdata = $this->opcache_filecache_only()) || false === strpos(nwdcx_normalizepath($file), nwdcx_normalizepath(ABSPATH))) {
793 return false;
794 }
795
796 $filem = nwdcx_normalizepath(rtrim($fcdata['file_cache'], '/\\').'/*/'.ltrim($file, '/\\').'.bin');
797 $match = glob($filem);
798 if (!empty($match) && \is_array($match)) {
799 foreach ($match as $fm) {
800 if (is_file($fm) && is_writable($fm)) {
801 @unlink($fm);
802 }
803 }
804 }
805
806 return true;
807 }
808
809 /**
810 * opcache_filecache_reset.
811 */
812 public function opcache_filecache_reset()
813 {
814 static $is_done = false;
815
816 $fcdata = $this->opcache_filecache_only();
817 if (!$is_done && !empty($fcdata)) {
818 $dir = $fcdata['file_cache'];
819 $cnt = 0;
820
821 $this->suspend_cache_write(true);
822
823 $max_execution_time = $this->get_max_execution_time(180);
824 $slowdown = 0;
825 foreach ($this->opcache_filecache_scanfiles($dir) as $object) {
826 try {
827 if ($object->isFile()) {
828 $file = nwdcx_normalizepath($object->getPathName());
829 if (false !== strpos($file, nwdcx_normalizepath(ABSPATH)) && is_writable($file)) {
830 @unlink($file);
831 ++$cnt;
832 }
833 }
834 } catch (\Throwable $e) {
835 nwdcx_throwable(__METHOD__, $e);
836 }
837
838 if ($slowdown > 10) {
839 $slowdown = 0;
840 usleep(5000);
841 }
842
843 ++$slowdown;
844
845 if ($max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $max_execution_time) {
846 break;
847 }
848 }
849
850 clearstatcache(true);
851
852 $is_done = true;
853
854 $this->suspend_cache_write(false);
855
856 return $cnt;
857 }
858
859 return false;
860 }
861
862 /**
863 * opcache_is_cached.
864 */
865 public function opcache_is_cached($file)
866 {
867 if (!$this->is_opcache_enable()) {
868 return -1;
869 }
870
871 $fcdata = $this->opcache_filecache_only();
872 if (!empty($fcdata) && false !== strpos(nwdcx_normalizepath($file), nwdcx_normalizepath(ABSPATH))) {
873 $filem = nwdcx_normalizepath(rtrim($fcdata['file_cache'], '/\\').'/*/'.ltrim($file, '/\\').'.bin');
874 $match = glob($filem);
875 if (!empty($match) && \is_array($match)) {
876 return true;
877 }
878 }
879
880 if (\function_exists('opcache_is_script_cached')) {
881 return @opcache_is_script_cached($file);
882 }
883
884 return -1;
885 }
886
887 /**
888 * opcache_flush.
889 */
890 public function opcache_flush($file)
891 {
892 if (!$this->is_php($file) || !@is_file($file) || !$this->is_opcache_enable()) {
893 return false;
894 }
895
896 if ($this->opcache_filecache_flush($file)) {
897 return true;
898 }
899
900 // wp 5.5
901 if (\function_exists('wp_opcache_invalidate')) {
902 return @wp_opcache_invalidate($file, true);
903 }
904
905 if (\function_exists('opcache_invalidate')) {
906 return @opcache_invalidate($file, true);
907 }
908
909 return false;
910 }
911
912 /**
913 * opcache_compile.
914 */
915 public function opcache_compile($file)
916 {
917 if (!$this->is_opcache_enable()) {
918 return false;
919 }
920
921 if (!empty($_GET['_wpnonce']) && !empty($_GET['action']) && !empty($_GET['page']) && 'docket-cache' === $_GET['page'] && 'docket-flush-opcache' === $_GET['action']) {
922 return -1;
923 }
924
925 if (\function_exists('opcache_compile_file') && $this->is_php($file) && @is_file($file) && false === $this->opcache_is_cached($file)) {
926 $this->touch($file, time() - 60);
927
928 try {
929 return @opcache_compile_file($file);
930 } catch (\Throwable $e) {
931 nwdcx_throwable(__METHOD__, $e);
932 }
933 }
934
935 return false;
936 }
937
938 /**
939 * opcache_reset.
940 */
941 public function opcache_reset()
942 {
943 if (!$this->is_opcache_enable()) {
944 return false;
945 }
946
947 try {
948 if (!@opcache_reset()) {
949 return $this->opcache_filecache_reset();
950 }
951 } catch (\Throwable $e) {
952 nwdcx_throwable(__METHOD__, $e);
953
954 return false;
955 }
956
957 // always true
958 return true;
959 }
960
961 /**
962 * opcache_cleanup.
963 */
964 public function opcache_cleanup()
965 {
966 add_action(
967 'shutdown',
968 function () {
969 $this->opcache_reset();
970 },
971 \PHP_INT_MAX
972 );
973 }
974
975 /**
976 * define_cache_path.
977 */
978 public function define_cache_path($cache_path)
979 {
980 $content_path = \defined('DOCKET_CACHE_CONTENT_PATH') ? DOCKET_CACHE_CONTENT_PATH : WP_CONTENT_DIR;
981 $content_path = nwdcx_normalizepath($content_path);
982
983 $cache_path = !empty($cache_path) && '/' !== $cache_path ? rtrim($cache_path, '/\\').'/' : $content_path.'/cache/docket-cache/';
984 $cache_path = nwdcx_normalizepath($cache_path);
985
986 if (!$this->is_docketcachedir($cache_path)) {
987 $cache_path = rtrim($cache_path, '/\\').'docket-cache/';
988 }
989
990 // create if not normal installation
991 if (false === strpos($content_path, '/wp-content/')) {
992 if (!@is_dir($cache_path)) {
993 $this->mkdir_p($cache_path);
994 }
995
996 if (!@is_dir($content_path)) {
997 $this->mkdir_p($content_path);
998 }
999 }
1000
1001 return $cache_path;
1002 }
1003
1004 /**
1005 * cachedir_flush.
1006 */
1007 public function cachedir_flush($dir, $cleanup = false, &$is_timeout = false)
1008 {
1009 static $is_done = false;
1010
1011 if ($is_done) {
1012 return 0;
1013 }
1014
1015 clearstatcache();
1016
1017 $dir = nwdcx_normalizepath(realpath($dir));
1018
1019 if (false === $dir || !@is_dir($dir) || !@is_writable($dir) || !$this->is_docketcachedir($dir)) {
1020 return false;
1021 }
1022
1023 if ($this->is_dirempty($dir)) {
1024 return true;
1025 }
1026
1027 $this->suspend_cache_write(true);
1028
1029 if (nwdcx_construe('WPCLI') && nwdcx_construe('WPCLI_OPCACHE')) {
1030 CliOpcache::set_bulk_flush(true);
1031 }
1032
1033 $gcisrun_lock = $dir.'/.gc-is-run.txt';
1034
1035 $max_execution_time = $this->get_max_execution_time(180);
1036 $flush_lock = DOCKET_CACHE_CONTENT_PATH.'/.object-cache-flush.txt';
1037 $flush_lock_expiry = time() + $max_execution_time + 10;
1038 if ($this->put($flush_lock, $flush_lock_expiry)) {
1039 $this->touch($flush_lock, $flush_lock_expiry);
1040 }
1041
1042 $slowdown = 0;
1043 $cnt = 0;
1044
1045 foreach ($this->scanfiles($dir) as $object) {
1046 try {
1047 if ($object->isFile()) {
1048 $this->unlink($object->getPathName(), $cleanup ? true : false);
1049 ++$cnt;
1050 }
1051 } catch (\Throwable $e) {
1052 nwdcx_throwable(__METHOD__, $e);
1053 }
1054
1055 if ($slowdown > 10) {
1056 $slowdown = 0;
1057 usleep(5000);
1058 }
1059
1060 ++$slowdown;
1061
1062 if ($max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $max_execution_time) {
1063 $is_timeout = true;
1064 break;
1065 }
1066 }
1067
1068 if ($cleanup) {
1069 // 24122020: deprecate
1070 $this->unlink($dir.'/index.php', true);
1071
1072 // placeholder
1073 $this->unlink($dir.'/index.html', true);
1074 }
1075
1076 $this->unlink($gcisrun_lock, true);
1077
1078 if (@is_file($flush_lock)) {
1079 @unlink($flush_lock);
1080 }
1081
1082 $this->suspend_cache_write(false);
1083 $is_done = true;
1084
1085 // When running under WP-CLI, notify the web server to run
1086 // opcache_reset() since CLI has a separate OPcache segment.
1087 if (nwdcx_construe('WPCLI') && nwdcx_construe('WPCLI_OPCACHE')) {
1088 CliOpcache::set_bulk_flush(false);
1089 CliOpcache::notify([]);
1090 }
1091
1092 return $cnt;
1093 }
1094
1095 /**
1096 * cache_size.
1097 */
1098 public function cache_size($dir)
1099 {
1100 static $is_done = false;
1101
1102 $bytestotal = 0;
1103 $fsizetotal = 0;
1104 $filestotal = 0;
1105
1106 clearstatcache();
1107 $gcisrun_lock = $dir.'/.gc-is-run.txt';
1108
1109 if (!$is_done && $this->is_docketcachedir($dir) && !@is_file(DOCKET_CACHE_CONTENT_PATH.'/.object-cache-flush.txt') && !@is_file($gcisrun_lock)) {
1110 // hardmax
1111 $maxfile = 999000; // 1000000 - 1000;
1112 $cnt = 0;
1113 $slowdown = 0;
1114
1115 ignore_user_abort(true);
1116 $max_execution_time = $this->get_max_execution_time(180);
1117
1118 $pattern = '@^([a-z0-9]{12})\-([a-z0-9]{12})\.php$@';
1119 foreach ($this->scanfiles($dir, null, $pattern) as $object) {
1120 try {
1121 if ($object->isFile()) {
1122 $fx = $object->getPathName();
1123
1124 if (!$this->remove_non_chunk_cache($dir, $fx)) {
1125 $fs = $object->getSize();
1126
1127 if ($cnt >= $maxfile) {
1128 $this->unlink($fx, true);
1129 } elseif (0 === $fs) {
1130 $this->unlink($fx, true);
1131 } else {
1132 $data = $this->cache_get($fx);
1133 if (false === $data) {
1134 $this->unlink($fx, true);
1135 } else {
1136 $bytestotal += \strlen(serialize($data));
1137 unset($data);
1138
1139 $fsizetotal += $fs;
1140
1141 ++$filestotal;
1142 ++$cnt;
1143 }
1144 }
1145 }
1146 }
1147 } catch (\Throwable $e) {
1148 nwdcx_throwable(__METHOD__, $e);
1149 }
1150
1151 if ($slowdown > 10) {
1152 $slowdown = 0;
1153 usleep(5000);
1154 }
1155
1156 ++$slowdown;
1157
1158 if ($max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $max_execution_time) {
1159 break;
1160 }
1161
1162 if (@is_file($gcisrun_lock)) {
1163 break;
1164 }
1165 }
1166 }
1167
1168 $is_done = true;
1169
1170 wp_cache_set('count_file', $filestotal, 'docketcache-gc', 86400);
1171
1172 return [
1173 'timestamp' => time(),
1174 'size' => $bytestotal,
1175 'filesize' => $fsizetotal,
1176 'files' => $filestotal,
1177 ];
1178 }
1179
1180 public function get_fatal_error_filename($file)
1181 {
1182 // sesimple yang mungkin.
1183 return $file.'-error.txt';
1184 }
1185
1186 public function has_fatal_error_before($file)
1187 {
1188 $file_fatal = $this->get_fatal_error_filename($file);
1189
1190 return @is_file($file_fatal);
1191 }
1192
1193 public function validate_fatal_error_file($file)
1194 {
1195 $file_fatal = $this->get_fatal_error_filename($file);
1196 if (!@is_file($file_fatal)) {
1197 return;
1198 }
1199
1200 if (!@is_file($file)) {
1201 @unlink($file_fatal);
1202
1203 return;
1204 }
1205
1206 $fm = time() - 10; // 10s
1207 if ($fm > @filemtime($file_fatal)) {
1208 if ($this->validate_file($file)) {
1209 @unlink($file_fatal);
1210
1211 return;
1212 }
1213
1214 // update timestamp
1215 $this->touch($file_fatal);
1216 }
1217 }
1218
1219 private function suspend_cache_file($file, $error, $seconds = 0)
1220 {
1221 $seconds = (int) $seconds;
1222 $file_fatal = $this->get_fatal_error_filename($file);
1223
1224 $errmsg = date('Y-m-d H:i:s T').\PHP_EOL.$error;
1225 if ($this->dump($file_fatal, $errmsg)) {
1226 if ($seconds > 0) {
1227 $this->touch($file, time() + $seconds);
1228 }
1229
1230 $this->dump(\dirname($file_fatal).'/last-error.txt', $errmsg);
1231
1232 return true;
1233 }
1234
1235 return false;
1236 }
1237
1238 private function capture_fatal_error()
1239 {
1240 register_shutdown_function(
1241 function () {
1242 $error = error_get_last();
1243 if ($error && \in_array($error['type'], [\E_ERROR, \E_PARSE, \E_COMPILE_ERROR, \E_USER_ERROR, \E_RECOVERABLE_ERROR, \E_CORE_ERROR], true)) {
1244 $file_error = $error['file'];
1245
1246 if ($this->is_docketcachefile($file_error)) {
1247 $this->dump($file_error, '<?php return false;');
1248
1249 $error['file'] = basename($error['file']);
1250 // 300s = 5m delay
1251 if ($this->suspend_cache_file($file_error, $this->export_var($error), 300)) {
1252 // refresh page if possible
1253 if ('cli' !== \PHP_SAPI && !wp_doing_ajax()) {
1254 echo '<script>document.body.innerHTML="";window.setTimeout(function() { window.location.assign(window.location.href); }, 750);</script>';
1255 }
1256 }
1257 }
1258 }
1259 }
1260 );
1261 }
1262
1263 /**
1264 * suspend_cache_write.
1265 */
1266 public function suspend_cache_write($suspend = null)
1267 {
1268 static $_suspend = false;
1269
1270 if (\is_bool($suspend)) {
1271 $_suspend = $suspend;
1272 }
1273
1274 return $_suspend;
1275 }
1276
1277 /**
1278 * cache_get.
1279 */
1280 public function cache_get($file)
1281 {
1282 $file = preg_replace('@(\.\.\/|\.\/)@', '', $file);
1283 if (!@is_file($file) || empty($this->filesize($file))) {
1284 return false;
1285 }
1286
1287 if (!$handle = @fopen($file, 'rb')) {
1288 return false;
1289 }
1290
1291 if ($this->has_fatal_error_before($file)) {
1292 return false;
1293 }
1294
1295 // capture non-throwable
1296 if (nwdcx_construe('CAPTURE_FATALERROR')) {
1297 $this->capture_fatal_error();
1298 }
1299
1300 // cache data
1301 $data = [];
1302
1303 // include when we can read, try to avoid fatal error.
1304 // LOCK_SH = shared lock
1305 if (flock($handle, \LOCK_SH)) {
1306 try {
1307 $data = @include $file;
1308 } catch (\Throwable $e) {
1309 $error = $e->getMessage();
1310
1311 $file_error = $e->getFile();
1312 if ($this->is_docketcachefile($file_error)) {
1313 $errmsg = 'E: '.$error.\PHP_EOL;
1314 $errmsg .= 'L: '.$e->getLine().\PHP_EOL;
1315 $errmsg .= 'F: '.basename($file_error).\PHP_EOL;
1316 $this->suspend_cache_file($file, $errmsg);
1317 }
1318
1319 $this->log('err', '000000000000-000000000000', 'cache_get: '.$error);
1320 $data = false;
1321 }
1322
1323 @flock($handle, \LOCK_UN);
1324 }
1325 @fclose($handle);
1326
1327 if (empty($data) || !isset($data['data'])) {
1328 return false;
1329 }
1330
1331 // 15052022
1332 /*if (false === $this->opcache_is_cached($file)) {
1333 $this->opcache_compile($file);
1334 }*/
1335
1336 return $data;
1337 }
1338
1339 /**
1340 * code_stub.
1341 */
1342 public function code_stub($data = '')
1343 {
1344 $is_debug = \defined('WP_DEBUG') && WP_DEBUG;
1345 $is_data = !empty($data);
1346 $is_precache = nwdcx_construe('PRECACHE');
1347 // capture class not exist.
1348 if (($is_debug || $is_precache) && empty($GLOBALS['DOCKET_CACHE_CODESTUB_FALSE'])) {
1349 $GLOBALS['DOCKET_CACHE_CODESTUB_FALSE'] = [];
1350 }
1351
1352 $ucode = '';
1353 if ($is_data && false !== strpos($data, 'Registry::p(')) {
1354 if (@preg_match_all('@Registry::p\(\'([a-zA-Z0-9_]+)\'\)@', $data, $mm)) {
1355 if (!empty($mm) && isset($mm[1]) && \is_array($mm[1])) {
1356 $cls = $mm[1];
1357 foreach ($cls as $clsname) {
1358 if ('stdClass' !== $clsname) {
1359 $clsfname = false;
1360 if ($is_debug) {
1361 $reflector = new \ReflectionClass($clsname);
1362 $clsfname = $reflector->getFileName();
1363 if (false !== $clsfname) {
1364 $clsfname = str_replace(ABSPATH, '', $clsfname);
1365 $ucode .= '/* f: '.$clsfname.' */'.\PHP_EOL;
1366 }
1367 }
1368
1369 // 13022023, self-note to future me.
1370 // The logic here, we can't just "include_once" a class if it doesn't exist
1371 // because if the main code uses "include" or "require" it will throw an error
1372 // since the class has already been loaded by us.
1373 $ucode .= "if(!@class_exists('".$clsname."',false)){";
1374 if ($is_debug || $is_precache) {
1375 $ucode .= "\$GLOBALS['DOCKET_CACHE_CODESTUB_FALSE'][__FILE__]=['".$clsname."','".$clsfname."'];";
1376 }
1377 $ucode .= 'return false;}'.\PHP_EOL;
1378 }
1379 }
1380 unset($cls, $clsname);
1381 }
1382 unset($mm);
1383 }
1384 }
1385
1386 $code = '<?php ';
1387 if ($is_data) {
1388 if (!empty($ucode) || false !== strpos($data, '\Nawawi\DocketCache\\')) {
1389 $code .= "if(!\defined('ABSPATH')){return false;}";
1390 }
1391 $code .= \PHP_EOL;
1392 if (!empty($ucode)) {
1393 $code .= $ucode;
1394 }
1395 $code .= 'return '.$data.';'.\PHP_EOL;
1396 $code .= '/*@DOCKET_CACHE_EOF*/';
1397 }
1398
1399 return $code;
1400 }
1401
1402 /**
1403 * log.
1404 */
1405 public function log($tag, $id, $data, $caller = '')
1406 {
1407 $do_flush = false;
1408 $file = nwdcx_constval('LOG_FILE');
1409 if (empty($file)) {
1410 return false;
1411 }
1412
1413 $logsize = nwdcx_constval('LOG_SIZE');
1414 if (empty($logsize) || !\is_int($logsize)) {
1415 $logsize = 0;
1416 }
1417
1418 if (is_multisite()) {
1419 $file = nwdcx_network_filepath($file);
1420 }
1421
1422 if (@is_file($file)) {
1423 if (nwdcx_construe('LOG_FLUSH') && 'flush' === $tag || ($logsize > 0 && $this->filesize($file) >= $logsize)) {
1424 $do_flush = true;
1425 }
1426 }
1427
1428 $timestamp = date('Y-m-d H:i:s T');
1429
1430 $rtag = trim($tag);
1431 if (\in_array($rtag, ['hit', 'miss', 'err', 'exp', 'del', 'info', 'dev'])) {
1432 $tag = str_pad($rtag, 5);
1433 }
1434 $log = '['.$timestamp.'] '.$tag.': "'.$id.'" "'.trim($data).'" "'.$caller.'"';
1435
1436 $flags = !$do_flush ? \LOCK_EX | \FILE_APPEND : \LOCK_EX;
1437 $do_chmod = !@is_file($file);
1438 if (@file_put_contents($file, $log.\PHP_EOL, $flags)) {
1439 if ($do_chmod) {
1440 $this->chmod($file);
1441 }
1442
1443 return true;
1444 }
1445
1446 return false;
1447 }
1448
1449 /**
1450 * sanitize_timestamp.
1451 */
1452 public function sanitize_timestamp($time)
1453 {
1454 $time = (int) $time;
1455 if ($time < 0) {
1456 $time = 0;
1457 } else {
1458 $max = ceil(log10($time));
1459 if ($max > 10 || 'NaN' === $max) {
1460 $time = 0;
1461 }
1462 }
1463
1464 return $time;
1465 }
1466
1467 /**
1468 * sanitize_maxttl.
1469 */
1470 public function sanitize_maxttl($seconds)
1471 {
1472 $seconds = $this->sanitize_timestamp($seconds);
1473
1474 // 86400 = 1d
1475 // 345600 = 4d
1476 // 2419200 = 28d
1477 if ($seconds < 86400) {
1478 $seconds = 345600;
1479 } elseif ($seconds > 2419200) {
1480 $seconds = 2419200;
1481 }
1482
1483 return $seconds;
1484 }
1485
1486 /**
1487 * sanitize_maxfile.
1488 */
1489 public function sanitize_maxfile($maxfile, $default = 50000)
1490 {
1491 $maxfile = (int) $maxfile;
1492 $min = 200;
1493 $max = 1000000;
1494 if (empty($maxfile)) {
1495 $maxfile = $default;
1496 }
1497
1498 if ($maxfile < $min) {
1499 $maxfile = $default;
1500 } elseif ($maxfile > $max) {
1501 $maxfile = $max;
1502 }
1503
1504 return $maxfile;
1505 }
1506
1507 /**
1508 * sanitize_precache_maxfile.
1509 */
1510 public function sanitize_precache_maxfile($maxfile)
1511 {
1512 $default = 100;
1513 if (empty($maxfile) || (int) $maxfile < 1) {
1514 return $default;
1515 }
1516
1517 return $this->sanitize_maxfile($maxfile, $default);
1518 }
1519
1520 /**
1521 * sanitize_maxsize.
1522 */
1523 public function sanitize_maxsize($bytes)
1524 {
1525 $min = 1048576; // 1M
1526 $max = 10485760; // 10M
1527 $bytes = (int) $bytes;
1528
1529 if ($bytes < $min) {
1530 return 3145728; // 3M
1531 }
1532
1533 if ($bytes > $max) {
1534 $bytes = $max;
1535 }
1536
1537 return $bytes;
1538 }
1539
1540 /**
1541 * sanitize_maxsizedisk.
1542 */
1543 public function sanitize_maxsizedisk($bytes)
1544 {
1545 if (empty($bytes) || !\is_int($bytes)) {
1546 return 524288000; // 500MB
1547 }
1548
1549 if ($bytes < 104857600) {
1550 $bytes = 104857600;
1551 }
1552
1553 return $bytes;
1554 }
1555
1556 /**
1557 * valid_timestamp.
1558 */
1559 public function valid_timestamp($timestamp)
1560 {
1561 $timestamp = $this->sanitize_timestamp($timestamp);
1562
1563 return $timestamp > 0;
1564 }
1565
1566 // put here because use in Becache.
1567 public function keys_alloptions()
1568 {
1569 // reference: wp-admin/includes/schema.php
1570 // exclude: rewrite_rules, active_plugins, uninstall_plugins, cron
1571 // exclude: widget_categories, widget_text, widget_rss
1572 return [
1573 'siteurl' => 1,
1574 'home' => 1,
1575 'blogname' => 1,
1576 'blogdescription' => 1,
1577 'users_can_register' => 1,
1578 'admin_email' => 1,
1579 'start_of_week' => 1,
1580 'use_balanceTags' => 1,
1581 'use_smilies' => 1,
1582 'require_name_email' => 1,
1583 'comments_notify' => 1,
1584 'posts_per_rss' => 1,
1585 'rss_use_excerpt' => 1,
1586 'mailserver_url' => 1,
1587 'mailserver_login' => 1,
1588 'mailserver_pass' => 1,
1589 'mailserver_port' => 1,
1590 'default_category' => 1,
1591 'default_comment_status' => 1,
1592 'default_ping_status' => 1,
1593 'default_pingback_flag' => 1,
1594 'posts_per_page' => 1,
1595 'date_format' => 1,
1596 'time_format' => 1,
1597 'links_updated_date_format' => 1,
1598 'comment_moderation' => 1,
1599 'moderation_notify' => 1,
1600 'permalink_structure' => 1,
1601 'hack_file' => 1,
1602 'blog_charset' => 1,
1603 'category_base' => 1,
1604 'ping_sites' => 1,
1605 'comment_max_links' => 1,
1606 'gmt_offset' => 1,
1607 'default_email_category' => 1,
1608 'template' => 1,
1609 'stylesheet' => 1,
1610 'comment_registration' => 1,
1611 'html_type' => 1,
1612 'use_trackback' => 1,
1613 'default_role' => 1,
1614 'db_version' => 1,
1615 'uploads_use_yearmonth_folders' => 1,
1616 'upload_path' => 1,
1617 'blog_public' => 1,
1618 'default_link_category' => 1,
1619 'show_on_front' => 1,
1620 'tag_base' => 1,
1621 'show_avatars' => 1,
1622 'avatar_rating' => 1,
1623 'upload_url_path' => 1,
1624 'thumbnail_size_w' => 1,
1625 'thumbnail_size_h' => 1,
1626 'thumbnail_crop' => 1,
1627 'medium_size_w' => 1,
1628 'medium_size_h' => 1,
1629 'avatar_default' => 1,
1630 'large_size_w' => 1,
1631 'large_size_h' => 1,
1632 'image_default_link_type' => 1,
1633 'image_default_size' => 1,
1634 'image_default_align' => 1,
1635 'close_comments_for_old_posts' => 1,
1636 'close_comments_days_old' => 1,
1637 'thread_comments' => 1,
1638 'thread_comments_depth' => 1,
1639 'page_comments' => 1,
1640 'comments_per_page' => 1,
1641 'default_comments_page' => 1,
1642 'comment_order' => 1,
1643 'timezone_string' => 1,
1644 'page_for_posts' => 1,
1645 'page_on_front' => 1,
1646 'default_post_format' => 1,
1647 'link_manager_enabled' => 1,
1648 'finished_splitting_shared_terms' => 1,
1649 'site_icon' => 1,
1650 'medium_large_size_w' => 1,
1651 'medium_large_size_h' => 1,
1652 'wp_page_for_privacy_policy' => 1,
1653 'show_comments_cookies_opt_in' => 1,
1654 'admin_email_lifespan' => 1,
1655 'initial_db_version' => 1,
1656 'fresh_site' => 1,
1657 'current_theme' => 1,
1658 'theme_switched' => 1,
1659 'generate_update_core_typography' => 1,
1660 'WPLANG' => 1,
1661 'new_admin_email' => 1,
1662 'recovery_mode_email_last_sent' => 1,
1663 'comment_previously_approved' => 1,
1664 'finished_updating_comment_type' => 1,
1665 'db_upgraded' => 1,
1666 /* wp >= 5.6.0 */
1667 'auto_update_core_dev' => 1,
1668 'auto_update_core_minor' => 1,
1669 'auto_update_core_major' => 1,
1670 /* wp >= 5.7 */
1671 'https_detection_errors' => 1,
1672 /* wp >= 5.8 */
1673 'wp_force_deactivated_plugins' => 1,
1674 ];
1675 }
1676
1677 /**
1678 * optimize_alloptions.
1679 */
1680 public function optimize_alloptions()
1681 {
1682 add_filter(
1683 'pre_cache_alloptions',
1684 function ($alloptions) {
1685 $wp_options = $this->keys_alloptions();
1686
1687 foreach ($alloptions as $key => $value) {
1688 // skip
1689 if (empty($value)) {
1690 continue;
1691 }
1692
1693 if (!\array_key_exists($key, $wp_options)) {
1694 unset($alloptions[$key]);
1695 }
1696 }
1697
1698 return $alloptions;
1699 },
1700 \PHP_INT_MIN
1701 );
1702 }
1703 }
1704