PluginProbe
Docket Cache – Object Cache Accelerator / 22.07.01
Docket Cache – Object Cache Accelerator v22.07.01
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 / Plugin.php

Plugin.php in Docket Cache – Object Cache Accelerator 22.07.01, at includes/src/Plugin.php

2,220 lines 73.2 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 final class Plugin extends Bepart
16 {
17 /**
18 * Plugin file.
19 *
20 * @var string
21 */
22 public $file;
23
24 /**
25 * Plugin slug.
26 *
27 * @var string
28 */
29 public $slug;
30
31 /**
32 * Plugin hook.
33 *
34 * @var string
35 */
36 public $hook;
37
38 /**
39 * Plugin path.
40 *
41 * @var string
42 */
43 public $path;
44
45 /**
46 * Plugin valid page uri.
47 *
48 * @var string
49 */
50 public $page;
51
52 /**
53 * Plugin action token.
54 *
55 * @var string
56 */
57 public $token;
58
59 /**
60 * Plugin action notice.
61 *
62 * @var string
63 */
64 public $notice;
65
66 /**
67 * Plugin screen name.
68 *
69 * @var string
70 */
71 public $screen;
72
73 /**
74 * The cache path.
75 *
76 * @var string
77 */
78 public $cache_path;
79
80 /**
81 * API Endpoint.
82 *
83 * @var string
84 */
85 public $api_endpoint;
86
87 /**
88 * Cronbot Endpoint.
89 *
90 * @var string
91 */
92 public $cronbot_endpoint;
93
94 /**
95 * WpConfig runtime notice.
96 *
97 * @var bool
98 */
99 public $inruntime = false;
100
101 /**
102 * constructor.
103 */
104 public function __construct()
105 {
106 $this->slug = 'docket-cache';
107 $this->file = nwdcx_constval('FILE');
108 $this->hook = plugin_basename($this->file);
109 $this->path = realpath(plugin_dir_path($this->file));
110 $this->register_init();
111 }
112
113 /**
114 * Dropino().
115 */
116 public function cx()
117 {
118 static $inst;
119 if (!\is_object($inst)) {
120 $inst = new Dropino($this->path);
121 }
122
123 return $inst;
124 }
125
126 /**
127 * Canopt().
128 */
129 public function co()
130 {
131 static $inst;
132 if (!\is_object($inst)) {
133 $inst = new Canopt();
134 }
135
136 return $inst;
137 }
138
139 /**
140 * Constans().
141 */
142 public function cf()
143 {
144 static $inst;
145 if (!\is_object($inst)) {
146 $inst = new Constans();
147 }
148
149 return $inst;
150 }
151
152 /**
153 * get_version.
154 */
155 public function version()
156 {
157 static $version = false;
158
159 if (!empty($version)) {
160 return $version;
161 }
162
163 $version = $this->plugin_meta($this->file)['Version'];
164
165 return $version;
166 }
167
168 /**
169 * get_info.
170 */
171 public function get_info()
172 {
173 $status_code = [
174 0 => esc_html__('Disabled', 'docket-cache'),
175 1 => esc_html__('Enabled', 'docket-cache'),
176 2 => esc_html__('Not Available', 'docket-cache'),
177 3 => esc_html__('Unknown', 'docket-cache'),
178 ];
179
180 $yesno = [
181 0 => esc_html__('No', 'docket-cache'),
182 1 => esc_html__('Yes', 'docket-cache'),
183 ];
184
185 $force_stats = $this->cf()->is_dctrue('WPCLI');
186 $cache_stats = $this->get_cache_stats($force_stats);
187
188 $status = $this->get_status();
189 $status_text_stats = '';
190 $status_text = '';
191
192 switch ($status) {
193 case 1:
194 if ($this->cf()->is_dctrue('STATS')) {
195 /* translators: %1$s = size, %2$s number of file */
196 $status_text_stats = sprintf(esc_html__(_n('%1$s object of %2$s file', '%1$s object of %2$s files', $cache_stats->files < 1 ? 1 : $cache_stats->files, 'docket-cache')), $this->normalize_size($cache_stats->size), $cache_stats->files);
197 }
198 $status_text = $status_code[1];
199 break;
200 case 2:
201 $status_text = esc_html__('Disabled at runtime.', 'docket-cache');
202 break;
203 default:
204 $status_text = $status_code[$status];
205 }
206
207 $opcache = $this->get_opcache_status();
208 $opcache_text_stats = '';
209 $opcache_text = '';
210
211 $opcache_dc_stats = '';
212 $opcache_wp_stats = '';
213 switch ($opcache->status) {
214 case 1:
215 /* translators: %1$s = size, %2$s number of file */
216 $opcache_text_stats = sprintf(esc_html__(_n('%1$s memory of %2$s file', '%1$s memory of %2$s files', $opcache->files, 'docket-cache')), $this->normalize_size($opcache->size), $opcache->files);
217
218 if ($opcache->dcfiles > 1) {
219 /* translators: %1$s = size, %2$s number of file */
220 $opcache_dc_stats = sprintf(esc_html__(_n('%1$s memory of %2$s file', '%1$s memory of %2$s files', $opcache->dcfiles, 'docket-cache')), $this->normalize_size($opcache->dcsize), $opcache->dcfiles);
221
222 /* translators: %1$s = size, %2$s number of file */
223 $opcache_wp_stats = sprintf(esc_html__(_n('%1$s memory of %2$s file', '%1$s memory of %2$s files', $opcache->wpfiles, 'docket-cache')), $this->normalize_size($opcache->wpsize), $opcache->wpfiles);
224 }
225 break;
226 case 2:
227 $opcache_text = $status_code[1];
228 break;
229 case 3:
230 $opcache_text = $status_code[1].' (<a href="https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache-only" rel="noopener" target="new">'.esc_html__('File cache only', 'docket-cache').'</a>)';
231 break;
232 case 4:
233 if ($opcache->files > 0) {
234 /* translators: %1$s = size, %2$s number of file */
235 $opcache_text_stats = sprintf(esc_html__(_n('%1$s size of %2$s file', '%1$s size of %2$s files', $opcache->files, 'docket-cache')), $this->normalize_size($opcache->size), $opcache->files);
236
237 if ($opcache->dcfiles > 1) {
238 /* translators: %1$s = size, %2$s number of file */
239 $opcache_dc_stats = sprintf(esc_html__(_n('%1$s size of %2$s file', '%1$s size of %2$s files', $opcache->dcfiles, 'docket-cache')), $this->normalize_size($opcache->dcsize), $opcache->dcfiles);
240
241 /* translators: %1$s = size, %2$s number of file */
242 $opcache_wp_stats = sprintf(esc_html__(_n('%1$s size of %2$s file', '%1$s size of %2$s files', $opcache->wpfiles, 'docket-cache')), $this->normalize_size($opcache->wpsize), $opcache->wpfiles);
243 }
244 } else {
245 $opcache_text = $status_code[1].' (<a href="https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache-only" rel="noopener" target="new">'.esc_html__('File cache only', 'docket-cache').'</a>)';
246 }
247
248 break;
249 default:
250 $opcache_text = $status_code[2];
251 }
252
253 $log_enable = $this->cf()->is_dctrue('LOG') ? 1 : 0;
254 $log_file = $this->cf()->dcvalue('LOG_FILE');
255
256 if (is_multisite()) {
257 $log_file = nwdcx_network_filepath($log_file);
258 }
259
260 $multisite_text = $status_code[3];
261 $multinets_lock = '';
262
263 if (is_multisite()) {
264 $netcount = get_networks(['count' => 1]);
265 $this->get_network_sites($sitecount, true);
266
267 /* translators: %s = sites */
268 $multisite_text = sprintf(esc_html__(_n('%s Site', '%s Sites', $sitecount, 'docket-cache')), $sitecount);
269
270 if ($netcount > 1) {
271 /* translators: %s = networks */
272 $multisite_text = $multisite_text.' '.sprintf(esc_html__(_n('of %s Network', 'of %s Networks', $netcount, 'docket-cache')), $netcount);
273
274 $multinets_lock = $this->sanitize_rootpath($this->cx()->multinet_tag());
275 }
276 }
277
278 $file_dropin = $this->cx()->resc()->dst;
279 if (@is_file($file_dropin)) {
280 $write_dropin = @is_writable($file_dropin);
281 } else {
282 $write_dropin = @is_writable($this->cx()->condir.'/');
283 }
284
285 $file_dropin_wp = $this->cx()->resc()->wpdst;
286 $dropin_wp_exist = false;
287
288 $is_dropin_alternative = $this->cx()->is_alternative();
289 if ($is_dropin_alternative) {
290 $dropin_wp_exist = @is_file($file_dropin_wp) || @is_link($file_dropin_wp);
291 }
292
293 $file_max = $this->get_cache_maxfile();
294 $disk_max = $this->normalize_size($this->get_cache_maxsize_disk());
295
296 $file_stats = $file_max;
297 $disk_stats = $disk_max;
298
299 if (isset($cache_stats->files) && !empty($cache_stats->files)) {
300 $file_stats = $cache_stats->files.' / '.$file_max;
301 }
302
303 if (isset($cache_stats->filesize) && !empty($cache_stats->filesize)) {
304 $disk_stats = $this->normalize_size($cache_stats->filesize).' / '.$disk_max;
305 }
306
307 return [
308 'status_code' => $status,
309 'status_text' => $status_text,
310 'status_text_stats' => $status_text_stats,
311 'opcache_code' => $opcache->status,
312 'opcache_text' => $opcache_text,
313 'opcache_text_stats' => $opcache_text_stats,
314 'opcache_dc_stats' => $opcache_dc_stats,
315 'opcache_wp_stats' => $opcache_wp_stats,
316 'php_memory_limit' => $this->normalize_size(@ini_get('memory_limit')),
317 'wp_memory_limit' => $this->normalize_size(WP_MEMORY_LIMIT),
318 'wp_max_memory_limit' => $this->normalize_size(WP_MAX_MEMORY_LIMIT),
319 'write_dropin' => $yesno[$write_dropin],
320 'dropin_path' => $this->sanitize_rootpath($file_dropin),
321 'dropin_isalt' => $is_dropin_alternative,
322 'dropin_alt' => $yesno[$is_dropin_alternative],
323 'dropin_wp' => $this->sanitize_rootpath($file_dropin_wp),
324 'dropin_wp_isexist' => $dropin_wp_exist,
325 'dropin_wp_exist' => $yesno[$dropin_wp_exist],
326 'write_cache' => $yesno[is_writable($this->cache_path)],
327 'cache_chunkdir' => $yesno[$this->cf()->dcvalue('CHUNKCACHEDIR')],
328 'cache_size' => $this->normalize_size($cache_stats->size),
329 'cache_path_real' => $this->cache_path,
330 'cache_path' => $this->sanitize_rootpath($this->cache_path),
331 'cache_maxfile' => $file_max,
332 'cache_file_stats' => $file_stats,
333 'cache_maxsize_disk' => $this->normalize_size($this->get_cache_maxsize_disk()),
334 'cache_disk_stats' => $disk_stats,
335 'log_file_real' => $log_file,
336 'log_file' => $this->sanitize_rootpath($log_file),
337 'log_enable' => $log_enable,
338 'log_enable_text' => $status_code[$log_enable],
339 'config_path' => $this->sanitize_rootpath($this->co()->path),
340 'write_config' => $yesno[$this->co()->is_options_writable()],
341 'wp_multisite' => $multisite_text,
342 'wp_multinetlock' => $multinets_lock,
343 'wp_multinetmain' => $yesno[is_main_network()],
344 ];
345 }
346
347 /**
348 * sanitize_rootpath.
349 */
350 public function sanitize_rootpath($path)
351 {
352 $wp_content_dir = wp_normalize_path(WP_CONTENT_DIR);
353 $abspath = wp_normalize_path(ABSPATH);
354
355 return rtrim(str_replace([$wp_content_dir, $abspath], ['/'.basename($wp_content_dir), '/'], $path), '/');
356 }
357
358 /**
359 * get_status.
360 */
361 public function get_status()
362 {
363 if ($this->cf()->is_dctrue('DISABLED')) {
364 return 2;
365 }
366
367 if (!$this->cx()->exists()) {
368 return 0;
369 }
370
371 if ($this->cx()->validate() && $this->cx()->multinet_me()) {
372 if ($this->cf()->is_dctrue('OBJECTCACHEOFF', true)) {
373 $this->co()->save('objectcacheoff', 'default');
374 }
375
376 return 1;
377 }
378
379 if (!$this->cx()->multinet_me()) {
380 return 0;
381 }
382
383 return 3;
384 }
385
386 /**
387 * get_logsize.
388 */
389 public function get_logsize()
390 {
391 if ($this->has_log($logfile)) {
392 return $this->normalize_size($this->filesize($logfile));
393 }
394
395 return 0;
396 }
397
398 /**
399 * get_opcache_status.
400 */
401 public function get_opcache_status($is_raw = false)
402 {
403 $total_bytes = 0;
404 $total_files = 0;
405 $status = 0;
406
407 $dcfiles = 0;
408 $dcbytes = 0;
409
410 $wpfiles = 0;
411 $wpbytes = 0;
412
413 $stale = 0;
414
415 $data = [];
416 if ($this->is_opcache_enable()) {
417 if (!$this->opcache_function_exists('opcache_get_status')) {
418 $status = 2;
419 } else {
420 $data = @opcache_get_status();
421 if (!empty($data) && \is_array($data)) {
422 if (!empty($data['opcache_enabled'])) {
423 if ($is_raw) {
424 return $data;
425 }
426
427 $status = 1;
428
429 if (!empty($data['memory_usage']['used_memory'])) {
430 $total_bytes = $data['memory_usage']['used_memory'];
431 }
432 if (!empty($data['opcache_statistics']['num_cached_scripts'])) {
433 $total_files = $data['opcache_statistics']['num_cached_scripts'];
434 }
435
436 if (!empty($data['scripts']) && \is_array($data['scripts'])) {
437 foreach ($data['scripts'] as $script => $arr) {
438 $cpath = $arr['full_path'];
439 if (!@is_file($cpath)) {
440 ++$stale;
441 }
442 $cfile = basename($cpath);
443 $cdir = \dirname($cpath);
444 if (false !== strpos($script, $this->cache_path) && $this->is_docketcachedir($cdir) && @preg_match('@^([a-z0-9]{12})\-([a-z0-9]{12})\.php$@', $cfile)) {
445 ++$dcfiles;
446 if (isset($arr['memory_consumption'])) {
447 $dcbytes += $arr['memory_consumption'];
448 }
449 } else {
450 ++$wpfiles;
451 if (isset($arr['memory_consumption'])) {
452 $wpbytes += $arr['memory_consumption'];
453 }
454 }
455 }
456 }
457 } elseif (!empty($data['file_cache_only'])) {
458 $status = 3;
459
460 if (!empty($data['file_cache']) && is_dir($data['file_cache']) && is_readable($data['file_cache'])) {
461 $dir = nwdcx_normalizepath(realpath($data['file_cache']));
462 $cnt = 0;
463
464 // dummy
465 $cdata = [
466 'opcache_statistics' => [
467 'num_cached_scripts' => 0,
468 ],
469 'scripts' => [],
470 ];
471
472 foreach ($this->opcache_filecache_scanfiles($dir) as $object) {
473 try {
474 if (!$object->isFile()) {
475 continue;
476 }
477
478 $cpath = nwdcx_normalizepath($object->getPathName());
479 if (false === strpos($cpath, nwdcx_normalizepath(ABSPATH))) {
480 continue;
481 }
482
483 $cfile = basename($cpath);
484 $cdir = \dirname($cpath);
485 $fs = $object->getSize();
486
487 if (false !== strpos($cpath, $this->cache_path) && $this->is_docketcachedir($cdir) && @preg_match('@^([a-z0-9]{12})\-([a-z0-9]{12})\.php\.bin$@', $cfile)) {
488 ++$dcfiles;
489 $dcbytes += $fs;
490 } else {
491 ++$wpfiles;
492 $wpbytes += $fs;
493 }
494
495 ++$cnt;
496
497 $total_bytes += $fs;
498
499 $cdata['scripts'][$cpath] = [
500 'full_path' => $cpath,
501 'memory_consumption' => $fs,
502 'last_used_timestamp' => $object->getATime(),
503 'hits' => 1,
504 ];
505 } catch (\Throwable $e) {
506 nwdcx_throwable(__METHOD__, $e);
507 continue;
508 }
509 }
510
511 if ($cnt > 0) {
512 $status = 4;
513 $total_files = $cnt;
514 }
515 }
516
517 if ($is_raw) {
518 $cdata['opcache_statistics']['num_cached_scripts'] = $cnt;
519 $data = array_merge($data, $cdata);
520 $data['_numfile'] = $cnt;
521 $data['_sizefile'] = $total_bytes;
522
523 return $data;
524 }
525 }
526 }
527 }
528 }
529
530 $arr = [
531 'status' => (int) $status,
532 'size' => $total_bytes,
533 'files' => (int) $total_files,
534 'wpfiles' => (int) $wpfiles,
535 'wpsize' => (int) $wpbytes,
536 'dcfiles' => (int) $dcfiles,
537 'dcsize' => (int) $dcbytes,
538 'stale' => (int) $stale,
539 ];
540
541 return (object) $arr;
542 }
543
544 /**
545 * get_cache_maxfile.
546 */
547 public function get_cache_maxfile()
548 {
549 $maxfile = $this->cf()->dcvalue('MAXFILE');
550
551 return $this->sanitize_maxfile($maxfile);
552 }
553
554 /**
555 * get_cache_maxttl.
556 */
557 public function get_cache_maxttl()
558 {
559 $maxttl = $this->cf()->dcvalue('MAXTTL');
560
561 return $this->sanitize_maxttl($maxttl);
562 }
563
564 /**
565 * get_cache_maxsize_disk.
566 */
567 public function get_cache_maxsize_disk()
568 {
569 $maxsizedisk = $this->cf()->dcvalue('MAXSIZE_DISK');
570
571 return $this->sanitize_maxsizedisk($maxsizedisk);
572 }
573
574 /**
575 * get_precache_maxfile.
576 */
577 public function get_precache_maxfile()
578 {
579 if ($this->cf()->is_dcfalse('PRECACHE')) {
580 return 0;
581 }
582
583 $maxfile = $this->cf()->dcvalue('PRECACHE_MAXFILE');
584
585 return $this->sanitize_precache_maxfile($maxfile);
586 }
587
588 /**
589 * get_cache_stats.
590 */
591 public function get_cache_stats($force = false)
592 {
593 $cache_stats = false;
594
595 if ($this->cf()->is_dctrue('STATS')) {
596 if ($force) {
597 $cache_stats = $this->cache_size($this->cache_path);
598 $this->co()->save_part($cache_stats, 'cachestats');
599 } else {
600 $cache_stats = $this->co()->get_part('cachestats');
601 }
602 }
603
604 if (empty($cache_stats) || !\is_array($cache_stats)) {
605 $cache_stats = [
606 'size' => 0,
607 'filesize' => 0,
608 'files' => 0,
609 ];
610 }
611
612 return (object) $cache_stats;
613 }
614
615 /**
616 * normalize_size.
617 */
618 public function normalize_size($size, $showb = true)
619 {
620 $size = wp_convert_hr_to_bytes($size);
621 $size = str_replace([',', ' ', 'B'], '', size_format($size));
622 if ($showb && is_numeric($size)) {
623 $size = $size.'B';
624 }
625
626 return $size;
627 }
628
629 public function site_url_scheme($site_url)
630 {
631 $site_url = trim($site_url);
632 if ('https://' !== substr($site_url, 0, 8) && $this->is_ssl()) {
633 $site_url = nwdcx_fixscheme($site_url, 'https://');
634 } elseif (!@preg_match('@^(https?:)?//@', $site_url)) {
635 $site_url = nwdcx_fixscheme($site_url, 'http://');
636 }
637
638 return rtrim($site_url, '/\\');
639 }
640
641 public function site_url($current = false, $is_home = false)
642 {
643 $option = $is_home ? 'home' : 'siteurl';
644 $site_url = get_option($option);
645 if (!$current && is_multisite()) {
646 $blog_id = get_main_site_id();
647 switch_to_blog($blog_id);
648 $site_url = get_option($option);
649 restore_current_blog();
650 }
651
652 return $this->site_url_scheme($site_url);
653 }
654
655 public function site_meta($short = false)
656 {
657 $m = '0,0';
658 if (is_multisite()) {
659 $n = get_networks(['count' => 1]);
660 $s = get_sites(['count' => 1]);
661 $m = $n.','.$s;
662 }
663
664 if ($short) {
665 $meta = $m.','.$this->version();
666 $meta = str_replace('0,0,', '', $meta);
667 $meta = str_replace('0', '', $meta);
668 } else {
669 $meta = $m.','.$this->version().','.$GLOBALS['wp_version'];
670 }
671
672 return str_replace('.', '', $meta);
673 }
674
675 /**
676 * flush_cache.
677 */
678 public function flush_cache($cleanup = false, &$is_timeout = false)
679 {
680 $this->co()->clear_part('cachestats');
681 $this->cx()->delay();
682
683 delete_expired_transients(true);
684
685 $cnt = $this->cachedir_flush($this->cache_path, $cleanup, $is_timeout);
686 if (false === $cnt) {
687 $this->cx()->undelay();
688
689 return $cnt;
690 }
691
692 return $cnt;
693 }
694
695 /**
696 * flush_fcache.
697 */
698 public function flush_fcache(&$file = '')
699 {
700 if (!empty($_GET['idxv'])) {
701 $fx = sanitize_text_field($_GET['idxv']);
702 $file = $this->cache_path.$fx.'.php';
703 if (@is_file($file)) {
704 $this->co()->clear_part('cachestats');
705
706 return $this->unlink($file, true);
707 }
708 }
709
710 return true;
711 }
712
713 /**
714 * has_log.
715 */
716 public function has_log(&$logfile = '')
717 {
718 $logfile = $this->cf()->dcvalue('LOG_FILE');
719
720 if (is_multisite()) {
721 $logfile = nwdcx_network_filepath($logfile);
722 }
723
724 return @is_file($logfile) && is_readable($logfile);
725 }
726
727 /**
728 * flush_log.
729 */
730 public function flush_log()
731 {
732 if ($this->has_log($logfile)) {
733 return @unlink($logfile);
734 }
735
736 return false;
737 }
738
739 public function switch_cron_site()
740 {
741 if (is_multisite()) {
742 $cronbot_siteid = $this->get_cron_siteid();
743 if (!empty($cronbot_siteid) && (int) $cronbot_siteid > 0) {
744 switch_to_blog($cronbot_siteid);
745
746 return true;
747 }
748 }
749
750 return false;
751 }
752
753 public function delete_cron_siteid($userid = false)
754 {
755 if (empty($userid)) {
756 $userid = get_current_user_id();
757 }
758
759 $key = 'cronbot-siteid-'.get_current_user_id();
760
761 return $this->co()->lookup_delete($key);
762 }
763
764 public function set_cron_siteid($id)
765 {
766 $key = 'cronbot-siteid-'.get_current_user_id();
767
768 return $this->co()->lookup_set($key, $id);
769 }
770
771 public function get_cron_siteid()
772 {
773 $key = 'cronbot-siteid-'.get_current_user_id();
774 $siteid = $this->co()->lookup_get($key);
775 if (empty($siteid)) {
776 $siteid = is_multisite() ? get_main_site_id() : get_current_blog_id();
777 }
778
779 return $siteid;
780 }
781
782 public function cleanuppost()
783 {
784 if (!nwdcx_wpdb($wpdb)) {
785 return false;
786 }
787
788 $sites = $this->get_network_sites();
789 $is_multisite = is_multisite();
790
791 $collect = [
792 'revision' => 0,
793 'autodraft' => 0,
794 'trashbin' => 0,
795 ];
796
797 $siteid = 0;
798 if ($is_multisite) {
799 $collect['site'] = \count($sites);
800
801 if (isset($_GET['siteid'])) {
802 $siteid = (int) sanitize_text_field($_GET['siteid']);
803 $this->set_current_select_siteid($siteid);
804 }
805 }
806
807 $max_execution_time = $this->get_max_execution_time();
808 $suppress = $wpdb->suppress_errors(true);
809 $doflush = false;
810 foreach ($sites as $num => $site) {
811 if ($is_multisite) {
812 if (!empty($siteid) && $siteid !== (int) $site['id']) {
813 continue;
814 }
815
816 switch_to_blog($site['id']);
817 }
818
819 $query = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_type = %s ORDER BY ID ASC LIMIT 1000", 'revision'));
820 if ($query) {
821 foreach ($query as $id) {
822 $id = (int) $id;
823 wp_delete_post_revision($id);
824 $doflush = true;
825 }
826 $collect['revision'] += \count($query);
827 }
828
829 $query = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_status = %s ORDER BY ID ASC LIMIT 1000", 'auto-draft'));
830 if ($query) {
831 foreach ($query as $id) {
832 $id = (int) $id;
833 wp_delete_post($id, true);
834 $doflush = true;
835 }
836 $collect['autodraft'] += \count($query);
837 }
838
839 $query = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_status = %s ORDER BY ID ASC LIMIT 1000", 'trash'));
840 if ($query) {
841 foreach ($query as $id) {
842 $id = (int) $id;
843 wp_delete_post($id, true);
844 $doflush = true;
845 }
846 $collect['trashbin'] += \count($query);
847 }
848
849 if ($is_multisite) {
850 restore_current_blog();
851 }
852
853 if ($max_execution_time > 0 && \defined('WP_START_TIMESTAMP') && (microtime(true) - WP_START_TIMESTAMP) > $max_execution_time) {
854 $is_timeout = true;
855 break;
856 }
857 }
858
859 $wpdb->suppress_errors($suppress);
860
861 if ($doflush) {
862 $this->flush_cache(false);
863 }
864
865 return (object) $collect;
866 }
867
868 public function delete_current_select_siteid($userid = false)
869 {
870 if (empty($userid)) {
871 $userid = get_current_user_id();
872 }
873
874 $key = 'current-select-siteid-'.get_current_user_id();
875
876 return $this->co()->lookup_delete($key);
877 }
878
879 public function get_current_select_siteid()
880 {
881 $key = 'current-select-siteid-'.get_current_user_id();
882
883 return (int) $this->co()->lookup_get($key);
884 }
885
886 public function set_current_select_siteid($id)
887 {
888 $key = 'current-select-siteid-'.get_current_user_id();
889
890 return $this->co()->lookup_set($key, $id);
891 }
892
893 /**
894 * compat_notice.
895 */
896 private function compat_notice()
897 {
898 if (!(\PHP_VERSION_ID >= 70205)) {
899 $text = __('Docket Cache plugin requires PHP 7.2.5 or greater.', 'docket-cache');
900
901 add_action(
902 'all_admin_notices',
903 function () use ($text) {
904 echo '<div id="docket-cache-notice" class="notice notice-warning is-dismissible"><p>'.$text.'</p></div>';
905 deactivate_plugins($this->hook);
906 wp_cache_flush();
907 },
908 \PHP_INT_MIN
909 );
910
911 if (\defined('WP_CLI') && WP_CLI) {
912 if (!\function_exists('deactivate_plugins')) {
913 include_once ABSPATH.'/wp-admin/includes/plugin.php';
914 }
915
916 if (\function_exists('deactivate_plugins')) {
917 deactivate_plugins($this->hook);
918 wp_cache_flush();
919
920 \WP_CLI::error($text, false);
921 \WP_CLI::halt(1);
922 }
923 }
924
925 return false;
926 }
927
928 return true;
929 }
930
931 /**
932 * cleanup.
933 */
934 private function deactivate_cleanup($is_uninstall = false)
935 {
936 WpConfig::unlink_runtime();
937
938 if ($this->cx()->validate()) {
939 $this->cx()->uninstall();
940 }
941
942 $this->cx()->undelay();
943
944 if ($this->cf()->is_dctrue('FLUSH_SHUTDOWN', true)) {
945 $this->cachedir_flush($this->cache_path, true);
946 }
947
948 $this->flush_log();
949
950 if ($is_uninstall) {
951 WpConfig::runtime_remove();
952
953 // reset on/off button
954 $this->co()->save('objectcacheoff', 'default');
955
956 // stats
957 $this->co()->clear_part('cachestats');
958
959 // cronbot/etc
960 $this->co()->save('cronbot', 'default');
961 $this->co()->clear_part('cronbot');
962 $this->co()->clear_part('pings');
963 $this->co()->clear_part('checkversion');
964
965 // clear all network if available
966 if (is_multisite()) {
967 $this->cx()->multinet_clear($this->cache_path, $this->cf()->dcvalue('LOG_FILE'));
968 }
969 }
970
971 if ($this->cf()->is_dctrue('OPCSHUTDOWN', true)) {
972 $this->opcache_cleanup();
973 }
974 }
975
976 /**
977 * uninstall.
978 */
979 public static function uninstall()
980 {
981 ( new self() )->deactivate_cleanup(true);
982 }
983
984 /**
985 * deactivate.
986 */
987 public function deactivate()
988 {
989 $this->deactivate_cleanup();
990 $this->unregister_cronjob();
991 }
992
993 /**
994 * activate.
995 */
996 public function activate()
997 {
998 if (!$this->compat_notice()) {
999 return;
1000 }
1001
1002 $this->flush_cache(false);
1003
1004 if ($this->cf()->is_dcfalse('OBJECTCACHEOFF', true)) {
1005 $this->cx()->install(true);
1006 }
1007
1008 $this->unregister_cronjob();
1009 }
1010
1011 /**
1012 * register_init.
1013 */
1014 private function register_init()
1015 {
1016 // unofficial constant: possible to disable nag notices
1017 !\defined('DISABLE_NAG_NOTICES') && \define('DISABLE_NAG_NOTICES', true);
1018
1019 $this->page = 'admin.php?page='.$this->slug;
1020 $this->screen = 'toplevel_page_docket-cache';
1021 $this->cache_path = $this->define_cache_path($this->cf()->dcvalue('PATH'));
1022
1023 if (is_multisite()) {
1024 $this->cache_path = nwdcx_network_dirpath($this->cache_path);
1025 }
1026
1027 $this->api_endpoint = 'https://api.docketcache.com';
1028 $this->cronbot_endpoint = 'https://cronbot.docketcache.com';
1029
1030 // use Constans() to trigger default
1031 if ($this->cf()->is_dctrue('DEV')) {
1032 $this->api_endpoint = 'http://api.docketcache.local';
1033 $this->cronbot_endpoint = 'http://cronbot.docketcache.local';
1034 }
1035
1036 $this->token = '';
1037 $this->notice = '';
1038 $this->inruntime = false;
1039
1040 add_filter(
1041 'perflab_oc_site_status_available_object_cache_services',
1042 function ($services) {
1043 $services[] = 'docket-cache';
1044
1045 return $services;
1046 },
1047 \PHP_INT_MAX
1048 );
1049 }
1050
1051 /**
1052 * critical_version.
1053 */
1054 private function critical_version()
1055 {
1056 $checkdata = $this->co()->get_part('checkversion', true);
1057 if (!empty($checkdata) && \is_array($checkdata) && !empty($checkdata['doversion'])) {
1058 $current_version = $this->plugin_meta($this->file)['Version'];
1059 if (0 === strcmp($checkdata['doversion'], $current_version)) {
1060 $this->flush_cache(true);
1061 }
1062 }
1063 unset($checkdata);
1064
1065 return true;
1066 }
1067
1068 /**
1069 * plugin_upgrade.
1070 */
1071 private function plugin_upgrade()
1072 {
1073 add_action(
1074 'shutdown',
1075 function () {
1076 $this->close_buffer();
1077
1078 if ($this->cf()->is_dcfalse('OBJECTCACHEOFF', true)) {
1079 $this->cx()->install(true);
1080 if (is_multisite()) {
1081 $this->cx()->multinet_install($this->hook);
1082 }
1083 }
1084
1085 // put last
1086 $this->critical_version();
1087 },
1088 \PHP_INT_MAX
1089 );
1090 }
1091
1092 /**
1093 * register_plugin_hooks.
1094 */
1095 private function register_plugin_hooks()
1096 {
1097 add_action(
1098 'plugins_loaded',
1099 function () {
1100 load_plugin_textdomain(
1101 'docket-cache',
1102 false,
1103 $this->path.'/languages/'
1104 );
1105 },
1106 0
1107 );
1108
1109 add_action(
1110 'upgrader_process_complete',
1111 function ($wp_upgrader, $options) {
1112 if ('update' !== $options['action']) {
1113 return;
1114 }
1115
1116 if ('plugin' === $options['type'] && !empty($options['plugins'])) {
1117 if (!\is_array($options['plugins'])) {
1118 return;
1119 }
1120
1121 foreach ($options['plugins'] as $plugin) {
1122 if ($plugin === $this->hook) {
1123 $this->plugin_upgrade();
1124 break;
1125 }
1126 }
1127 }
1128 },
1129 \PHP_INT_MAX,
1130 2
1131 );
1132
1133 // wp 5.5 >=
1134 if (\function_exists('wp_is_maintenance_mode')) {
1135 add_action(
1136 'upgrader_overwrote_package',
1137 function ($package, $package_data, $package_type = 'plugin') {
1138 if (!empty($package_data) && \is_array($package_data) && !empty($package_data['TextDomain']) && $this->slug === $package_data['TextDomain']) {
1139 $this->plugin_upgrade();
1140 }
1141 },
1142 \PHP_INT_MAX,
1143 3
1144 );
1145 }
1146
1147 add_action(
1148 'admin_footer',
1149 function () {
1150 $output = $this->cx()->after_delay();
1151 if (!empty($output)) {
1152 echo $output;
1153 }
1154 },
1155 \PHP_INT_MAX
1156 );
1157
1158 if (!is_admin() && $this->cf()->is_dctrue('SIGNATURE')) {
1159 add_action(
1160 'send_headers',
1161 function () {
1162 $status = $this->cx()->validate() ? 'on' : 'off';
1163 header('x-'.$this->slug.': '.$status.'; '.$this->site_meta(true));
1164 },
1165 \PHP_INT_MAX
1166 );
1167 }
1168
1169 if (!empty($_SERVER['REQUEST_URI']) && false !== strpos($_SERVER['REQUEST_URI'], '/admin.php?page=docket-cache')) {
1170 add_action(
1171 'plugins_loaded',
1172 function () {
1173 if (!headers_sent() && !empty($_SERVER['REQUEST_URI'])) {
1174 if ($this->cf()->is_dctrue('LOG')) {
1175 $req = $_SERVER['REQUEST_URI'];
1176 if ((false !== strpos($req, '?page=docket-cache&idx=log&dl=0') || false !== strpos($req, '?page=docket-cache-log&idx=log&dl=0'))
1177 && preg_match('@log\&dl=\d+$@', $req)) {
1178 $file = $this->cf()->dcvalue('LOG_FILE');
1179
1180 if (is_multisite()) {
1181 $file = nwdcx_network_filepath($file);
1182 }
1183
1184 @header('Cache-Control: no-cache, no-store, must-revalidate, max-age=0');
1185 @header('Content-Type: text/plain; charset=UTF-8');
1186 if (@is_file($file) && @is_readable($file)) {
1187 @readfile($file);
1188 $this->close_exit();
1189 }
1190
1191 $this->close_exit(__('No data available', 'docket-cache'));
1192 }
1193 }
1194
1195 if (\defined('WP_DEBUG') && WP_DEBUG && \defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
1196 $req = $_SERVER['REQUEST_URI'];
1197 if ((false !== strpos($req, '?page=docket-cache&idx=config&wplog=0') || false !== strpos($req, '?page=docket-cache-config&idx=config&wplog=0'))
1198 && preg_match('@config\&wplog=\d+(\&dd=\d+)?$@', $req)) {
1199 $file = ini_get('error_log');
1200
1201 @header('Cache-Control: no-cache, no-store, must-revalidate, max-age=0');
1202 @header('Content-Type: text/plain; charset=UTF-8');
1203 if (@is_file($file) && @is_readable($file)) {
1204 if ($this->cf()->is_dctrue('TRYREADWPDEBUGLOG') || !empty($_GET['dd']) && 1 === (int) $_GET['dd']) {
1205 $log = file_get_contents($file);
1206 if (!empty($log)) {
1207 $log = str_replace(['undefined function', 'Stack trace'], ['undefined-function', 'Stack-trace'], $log);
1208 }
1209 $this->close_exit($log);
1210 }
1211
1212 @readfile($file);
1213 $this->close_exit();
1214 }
1215
1216 $this->close_exit(__('No data available', 'docket-cache'));
1217 }
1218 }
1219 }
1220 },
1221 \PHP_INT_MIN
1222 );
1223 }
1224
1225 add_action(
1226 'plugins_loaded',
1227 function () {
1228 if (nwdcx_wpdb($wpdb)) {
1229 if ($this->co()->lockexp('sqlbigselect')) {
1230 return;
1231 }
1232
1233 $suppress = $wpdb->suppress_errors(true);
1234 $ok = $wpdb->get_var('SELECT @@SESSION.SQL_BIG_SELECTS LIMIT 1');
1235 if (empty($ok)) {
1236 $wpdb->query('SET SESSION SQL_BIG_SELECTS=1');
1237 } else {
1238 // already big select, lock 24h
1239 $locktime = time() + 86400;
1240 $this->co()->setlock('sqlbigselect', $locktime);
1241 }
1242
1243 $wpdb->suppress_errors($suppress);
1244 }
1245 },
1246 \PHP_INT_MIN
1247 );
1248
1249 add_filter(
1250 'auto_update_plugin',
1251 function ($update, $item) {
1252 if ('docket-cache' === $item->slug) {
1253 return $this->cf()->is_dctrue('AUTOUPDATE');
1254 }
1255
1256 return $update;
1257 },
1258 \PHP_INT_MAX,
1259 2
1260 );
1261
1262 if (class_exists('Nawawi\\DocketCache\\CronAgent')) {
1263 ( new CronAgent($this) )->register();
1264 }
1265
1266 register_activation_hook($this->hook, [$this, 'activate']);
1267 register_deactivation_hook($this->hook, [$this, 'deactivate']);
1268 register_uninstall_hook($this->hook, [__CLASS__, 'uninstall']);
1269 }
1270
1271 /**
1272 * action_query.
1273 */
1274 public function action_query($key, $args_extra = [])
1275 {
1276 $key = str_replace('docket-', '', $key);
1277 $key = 'docket-'.$key;
1278
1279 $args = array_merge(
1280 [
1281 'action' => $key,
1282 ],
1283 $args_extra
1284 );
1285
1286 // last
1287 $args['st'] = time();
1288
1289 $page = $this->page;
1290 if (!empty($args['idx']) && $this->is_subpage($args['idx'])) {
1291 $page = $page.'-'.$args['idx'];
1292 }
1293
1294 $query = add_query_arg($args, $page);
1295
1296 return wp_nonce_url(network_admin_url($query), $key);
1297 }
1298
1299 /**
1300 * action_field. Use at addons.
1301 */
1302 public function action_field($key, $args = [])
1303 {
1304 $key = str_replace('docket-', '', $key);
1305 $key = 'docket-'.$key;
1306
1307 $field = wp_nonce_field($key, '_wpnonce', false, false);
1308 $field .= '<input type="hidden" name="action" value="'.$key.'">';
1309 if (!empty($args) && \is_array($args)) {
1310 foreach ($args as $n => $v) {
1311 if ('page' === $n) {
1312 $v = 'docket-cache-'.$v;
1313 }
1314 $field .= '<input type="hidden" name="'.$n.'" value="'.$v.'">';
1315 }
1316 }
1317
1318 return $field;
1319 }
1320
1321 /**
1322 * is_subpage.
1323 */
1324 public function is_subpage($index)
1325 {
1326 if ('mods' === substr($index, 0, 4)) {
1327 return true;
1328 }
1329
1330 $subpage = [
1331 'config' => 1,
1332 'log' => 1,
1333 'cronbot' => 1,
1334 'opcviewer' => 1,
1335 ];
1336
1337 return \array_key_exists($index, $subpage);
1338 }
1339
1340 /**
1341 * get_subpage.
1342 */
1343 public function get_subpage()
1344 {
1345 if (!empty($_GET['page'])) {
1346 if ('docket-cache-' === substr($_GET['page'], 0, 13)) {
1347 $index = substr($_GET['page'], 13);
1348 if (!empty($index) && $this->is_subpage($index)) {
1349 return $index;
1350 }
1351 } elseif ('docket-cache' === $_GET['page'] && !empty($_GET['idx']) && 'mods' === substr($_GET['idx'], 0, 4)) {
1352 return $_GET['idx'];
1353 }
1354 }
1355
1356 return false;
1357 }
1358
1359 /**
1360 * get_screen.
1361 */
1362 public function get_screen()
1363 {
1364 $screen = $this->screen;
1365 $index = $this->get_subpage();
1366 if (!empty($index)) {
1367 $screen = $this->slug.'_page_'.$this->slug.'-'.$index;
1368 }
1369
1370 return $screen;
1371 }
1372
1373 /**
1374 * get_page.
1375 */
1376 public function get_page($args = [])
1377 {
1378 $page = $this->page;
1379 $index = $this->get_subpage();
1380 if (!empty($index)) {
1381 $page = $page.'-'.$index;
1382 }
1383
1384 if (!empty($args) && \is_array($args)) {
1385 $query = http_build_query($args);
1386 if (false !== strpos($page, '?')) {
1387 $page = rtrim($page, '&').'&'.$query;
1388 } else {
1389 $page = $page.'?'.$query;
1390 }
1391 }
1392
1393 return $page;
1394 }
1395
1396 /**
1397 * our_screen.
1398 */
1399 public function our_screen()
1400 {
1401 $current_screen = get_current_screen()->id;
1402 if (substr($current_screen, 0, \strlen($this->screen)) === $this->screen) {
1403 return true;
1404 }
1405
1406 $subsplug = $this->slug.'_page_'.$this->slug.'-';
1407 if (substr($current_screen, 0, \strlen($subsplug)) === $subsplug) {
1408 return true;
1409 }
1410
1411 return false;
1412 }
1413
1414 /**
1415 * register_admin_hooks.
1416 */
1417 private function register_admin_hooks()
1418 {
1419 $action_name = is_multisite() ? 'network_admin_menu' : 'admin_menu';
1420 add_action(
1421 $action_name,
1422 function () {
1423 $cap = is_multisite() ? 'manage_network_options' : 'manage_options';
1424 $order = is_multisite() ? '25.1' : '80.1';
1425 $view = new View($this);
1426
1427 add_menu_page(
1428 'Docket Cache',
1429 'Docket Cache',
1430 $cap,
1431 $this->slug,
1432 [$view, 'index'],
1433 Resc::iconmenu(),
1434 $order
1435 );
1436
1437 $title = esc_html__('Overview', 'docket-cache');
1438 add_submenu_page(
1439 $this->slug,
1440 $title,
1441 $title,
1442 $cap,
1443 $this->slug,
1444 [$view, 'index']
1445 );
1446
1447 if ($this->cf()->is_dctrue('CRONBOT')) {
1448 $title = esc_html__('Cronbot', 'docket-cache');
1449 add_submenu_page(
1450 $this->slug,
1451 $title,
1452 $title,
1453 $cap,
1454 $this->slug.'-cronbot',
1455 [$view, 'index']
1456 );
1457 }
1458
1459 if ($this->cf()->is_dctrue('OPCVIEWER')) {
1460 $title = esc_html__('OPcache', 'docket-cache');
1461 add_submenu_page(
1462 $this->slug,
1463 $title,
1464 $title,
1465 $cap,
1466 $this->slug.'-opcviewer',
1467 [$view, 'index']
1468 );
1469 }
1470
1471 do_action('docketcache/view/submenubefore', $this->slug, $cap, $view);
1472
1473 if ($this->cf()->is_dctrue('LOG')) {
1474 $title = esc_html__('Cache Log', 'docket-cache');
1475 add_submenu_page(
1476 $this->slug,
1477 $title,
1478 $title,
1479 $cap,
1480 $this->slug.'-log',
1481 [$view, 'index']
1482 );
1483 }
1484
1485 $title = esc_html__('Configuration', 'docket-cache');
1486 add_submenu_page(
1487 $this->slug,
1488 $title,
1489 $title,
1490 $cap,
1491 $this->slug.'-config',
1492 [$view, 'index']
1493 );
1494
1495 do_action('docketcache/view/submenuafter', $this->slug, $cap, $view);
1496 }
1497 );
1498
1499 add_action(
1500 'admin_bar_menu',
1501 function ($admin_bar) {
1502 if (!is_multisite() || !current_user_can('manage_network_options')) {
1503 return;
1504 }
1505
1506 $admin_bar->add_menu(
1507 [
1508 'id' => 'network-admin-docketcache',
1509 'parent' => 'network-admin',
1510 'group' => null,
1511 'title' => 'Docket Cache',
1512 'href' => network_admin_url($this->page),
1513 'meta' => [
1514 'title' => 'Docket Cache',
1515 ],
1516 ]
1517 );
1518
1519 if (nwdcx_network_multi()) {
1520 $networks = get_networks();
1521 if (!empty($networks) && \is_array($networks)) {
1522 foreach ($networks as $network) {
1523 $id = $network->id;
1524 $url = $this->site_url_scheme('http://'.$network->domain.$network->path);
1525 $admin_bar->add_menu(
1526 [
1527 'id' => 'network-admin-docketcache-'.$id,
1528 'parent' => 'network-admin-'.$id,
1529 'group' => null,
1530 'title' => 'Docket Cache',
1531 'href' => $url.'/wp-admin/network/'.$this->page,
1532 'meta' => [
1533 'title' => 'Docket Cache',
1534 ],
1535 ]
1536 );
1537 }
1538 }
1539 }
1540 },
1541 \PHP_INT_MAX
1542 );
1543
1544 add_action(
1545 'in_admin_header',
1546 function () {
1547 if ($this->our_screen()) {
1548 remove_all_actions('admin_notices');
1549 remove_all_actions('all_admin_notices');
1550 }
1551
1552 add_action(
1553 'all_admin_notices',
1554 function () {
1555 if (!current_user_can(is_multisite() ? 'manage_network_options' : 'manage_options')) {
1556 return;
1557 }
1558
1559 if (!$this->our_screen() && false === strpos($_SERVER['REQUEST_URI'], '/plugins.php') && false === strpos($_SERVER['REQUEST_URI'], '/update-core.php')) {
1560 return;
1561 }
1562
1563 if ($this->cx()->exists()) {
1564 $url = $this->action_query('update-dropino');
1565 $urld = $this->action_query('dismiss-dropino');
1566
1567 if ($this->cx()->validate()) {
1568 if ($this->cx()->is_outdated() && !$this->cx()->install(true)) {
1569 /* translators: %s: url */
1570 $message = sprintf(__('The object-cache.php Drop-In is outdated. Please click <strong>Re-Install</strong> to update it now. <br><br><a href="%s" style="min-width:100px;text-align:center;font-wight:bold;" class="button button-primary">Re-Install</a>', 'docket-cache'), $url);
1571 }
1572 } else {
1573 /* translators: %1$s: url install, %2$s = url dismiss */
1574 $message = $this->cf()->is_dctrue('OBJECTCACHEOFF', true) ? '' : sprintf(__('An unknown object-cache.php Drop-In was found. Please click <strong>Install</strong> to use <strong>Docket Cache</strong>. <br><br><a href="%1$s" style="min-width:100px;text-align:center;font-weight:bold;" class="button button-primary button-small">Install</a>&nbsp;<a href="%2$s" style="min-width:100px;text-align:center;font-weight:bold;" class="button button-secondary button-small">Dismiss</a>', 'docket-cache'), $url, $urld);
1575 }
1576 }
1577
1578 if (2 === $this->get_status() && $this->our_screen()) {
1579 $message = esc_html__('The Object Cache feature has been disabled at runtime.', 'docket-cache');
1580 }
1581
1582 if (isset($message)) {
1583 echo Resc::boxmsg($message, 'warning', false, false, false);
1584 }
1585 }
1586 );
1587 },
1588 \PHP_INT_MAX
1589 );
1590
1591 add_action(
1592 'admin_enqueue_scripts',
1593 function ($hook) {
1594 $is_debug = $this->cf()->is_true('WP_DEBUG');
1595 $plugin_url = plugin_dir_url($this->file);
1596 $version = str_replace('.', '', $this->version()).'xe'.($is_debug ? date('his') : date('yd'));
1597 wp_enqueue_script($this->slug.'-worker', $plugin_url.'includes/admin/worker.js', ['jquery'], $version, false);
1598 wp_localize_script(
1599 $this->slug.'-worker',
1600 'docket_cache_config',
1601 [
1602 'ajaxurl' => admin_url('admin-ajax.php'),
1603 'token' => wp_create_nonce('docketcache-token-nonce'),
1604 'slug' => $this->slug,
1605 'debug' => $is_debug ? 'true' : 'false',
1606 ]
1607 );
1608
1609 if ($hook === $this->screen || $this->our_screen()) {
1610 wp_enqueue_style($this->slug.'-core', $plugin_url.'includes/admin/docket.css', null, $version);
1611 wp_enqueue_script($this->slug.'-core', $plugin_url.'includes/admin/docket.js', ['jquery'], $version, true);
1612 }
1613
1614 if ($this->cf()->is_dctrue('PAGELOADER')) {
1615 wp_enqueue_style($this->slug.'-loader', $plugin_url.'includes/admin/pageloader.css', null, $version);
1616 wp_enqueue_script($this->slug.'-loader', $plugin_url.'includes/admin/pageloader.js', ['jquery'], $version, true);
1617 }
1618 }
1619 );
1620
1621 // refresh user_meta: after logout
1622 add_action(
1623 'wp_logout',
1624 function () {
1625 add_action(
1626 'shutdown',
1627 function () {
1628 $this->close_buffer();
1629 $user = wp_get_current_user();
1630 if (\is_object($user) && isset($user->ID)) {
1631 wp_cache_delete($user->ID, 'user_meta');
1632 $this->delete_cron_siteid($user->ID);
1633 $this->delete_current_select_siteid($user->ID);
1634 }
1635 },
1636 \PHP_INT_MAX
1637 );
1638 },
1639 \PHP_INT_MAX
1640 );
1641
1642 add_action(
1643 'wp_ajax_docket_worker',
1644 function () {
1645 if (!check_ajax_referer('docketcache-token-nonce', 'token', false) && !isset($_POST['type'])) {
1646 wp_send_json_error('Invalid security token sent.');
1647 exit;
1648 }
1649
1650 $type = sanitize_text_field($_POST['type']);
1651
1652 if ($this->cx()->validate()) {
1653 if ('preload' === $type) {
1654 $this->send_json_continue($this->slug.':worker: pong '.$type);
1655 $this->cx()->undelay();
1656 do_action('docketcache/action/preload/objectcache');
1657 exit;
1658 }
1659
1660 if ('fetch' === $type) {
1661 $this->send_json_continue($this->slug.':worker: pong '.$type);
1662 @Crawler::fetch_home();
1663 exit;
1664 }
1665
1666 if ('countcachesize' === $type) {
1667 do_action('docketcache/action/countcachesize');
1668
1669 $info = (object) $this->get_info();
1670
1671 $cache_stats = 1 === $info->status_code && !empty($info->status_text_stats) ? $info->status_text_stats : $info->status_text;
1672 $opcache_stats = 1 === $info->opcache_code && !empty($info->opcache_text_stats) ? $info->opcache_text_stats : $info->opcache_text;
1673 $opcache_dc_stats = !empty($info->opcache_dc_stats) ? $info->opcache_dc_stats : esc_html__('Not Available', 'docket-cache');
1674 $opcache_wp_stats = !empty($info->opcache_wp_stats) ? $info->opcache_wp_stats : esc_html__('Not Available', 'docket-cache');
1675
1676 $response = [];
1677 $response = ['success' => true];
1678 $response['data'] = $this->slug.':worker: pong '.$type;
1679
1680 $stats = [];
1681 $stats['obc'] = '0B' !== substr($cache_stats, 0, 2) ? $cache_stats : esc_html__('Not Available', 'docket-cache');
1682 $stats['opc'] = $opcache_stats;
1683 $stats['obcs'] = '0B' !== substr($info->status_text_stats, 0, 2) ? $info->status_text_stats : esc_html__('Not Available', 'docket-cache');
1684 $stats['opcs'] = $info->opcache_text_stats;
1685 $stats['opcdc'] = $opcache_dc_stats;
1686 $stats['opcwp'] = $opcache_wp_stats;
1687
1688 $stats['ofile'] = $info->cache_file_stats;
1689 $stats['odisk'] = $info->cache_disk_stats;
1690
1691 $response['cachestats'] = $stats;
1692 wp_send_json($response);
1693 exit;
1694 }
1695 }
1696
1697 if ('flush' === $type) {
1698 $this->send_json_continue($this->slug.':worker: pong '.$type);
1699 delete_expired_transients(true);
1700 exit;
1701 }
1702
1703 wp_send_json_error($this->slug.':worker: "'.$type.'" not available');
1704 exit;
1705 }
1706 );
1707
1708 add_filter(
1709 'admin_footer_text',
1710 function ($text) {
1711 if ($this->our_screen()) {
1712 $meta = $this->plugin_meta($this->file);
1713 /* translators: %s: version */
1714 $text = $meta['Name'].' '.sprintf(__('Version %s', 'docket-cache'), $meta['Version']);
1715 $text = apply_filters('docketcache/filter/footerversion', $text);
1716 }
1717
1718 return $text;
1719 },
1720 \PHP_INT_MAX
1721 );
1722
1723 foreach (['update_footer', 'core_update_footer'] as $fn) {
1724 add_filter(
1725 $fn,
1726 function ($text) {
1727 if ($this->our_screen()) {
1728 /* translators: %s: version */
1729 $text = 'WordPress '.' '.sprintf(__('Version %s', 'docket-cache'), $GLOBALS['wp_version']);
1730 }
1731
1732 return $text;
1733 },
1734 \PHP_INT_MAX
1735 );
1736 }
1737
1738 $filter_name = sprintf('%splugin_action_links_%s', is_multisite() ? 'network_admin_' : '', $this->hook);
1739 add_filter(
1740 $filter_name,
1741 function ($links) {
1742 $new = [
1743 'docket-cache-overview' => sprintf('<a href="%s">%s</a>', network_admin_url($this->page), __('Overview', 'docket-cache')),
1744 'docket-cache-configuration' => sprintf('<a href="%s">%s</a>', network_admin_url($this->page.'-config'), __('Configure', 'docket-cache')),
1745 ];
1746
1747 return array_merge($new, $links);
1748 }
1749 );
1750
1751 add_filter(
1752 'plugin_row_meta',
1753 function ($plugin_meta, $plugin_file) {
1754 if ($plugin_file === $this->hook) {
1755 $row_meta = [
1756 'docs' => '<a href="https://docs.docketcache.com/?utm_source=wp-plugins&utm_campaign=doc-uri&utm_medium=wp-dash" target="new" rel="noopener">'.__('Docs', 'docket-cache').'</a>',
1757 'sponsor' => '<a href="https://docketcache.com/sponsorship/?utm_source=wp-plugins&utm_campaign=sponsor-uri&utm_medium=wp-dash" target="new" rel="noopener"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>'.__('Sponsor', 'docket-cache').'</a>',
1758 ];
1759 $plugin_meta = array_merge($plugin_meta, $row_meta);
1760 }
1761
1762 return $plugin_meta;
1763 },
1764 10,
1765 2
1766 );
1767
1768 // reference: Canopt::save()
1769 add_action(
1770 'docketcache/action/saveoption',
1771 function ($name, $value, $status = true) {
1772 switch ($name) {
1773 case 'log':
1774 if (true === $status) {
1775 $this->flush_log();
1776 }
1777 if ('enable' === $value) {
1778 @Crawler::fetch_home();
1779 }
1780 break;
1781 case 'wpoptaload':
1782 add_action(
1783 'shutdown',
1784 function () {
1785 $this->close_buffer();
1786
1787 wp_cache_delete('alloptions', 'options');
1788 if (\function_exists('wp_cache_flush_group')) {
1789 wp_cache_flush_group('options');
1790 wp_cache_flush_group('docketcache-precache');
1791 }
1792 },
1793 \PHP_INT_MAX
1794 );
1795
1796 break;
1797 case 'cronoptmzdb':
1798 $this->unregister_cronjob();
1799 break;
1800 case 'cronbot':
1801 $action = 'enable' === $value ? true : false;
1802 add_action(
1803 'shutdown',
1804 function () use ($action) {
1805 if (!$action) {
1806 $this->close_buffer();
1807
1808 apply_filters('docketcache/filter/active/cronbot', $action);
1809 }
1810 },
1811 \PHP_INT_MAX
1812 );
1813 break;
1814 case 'rtwpdebug':
1815 case 'rtwpdebuglog':
1816 $error_log = ini_get('error_log');
1817 if ('off' === $value && @is_file($error_log) && @is_writable($error_log)) {
1818 @unlink($error_log);
1819 }
1820 break;
1821 case 'menucache':
1822 wp_cache_flush_group('docketcache-menu');
1823 break;
1824 }
1825
1826 if (WpConfig::is_runtimeconst($name)) {
1827 WpConfig::write_runtime();
1828 }
1829 },
1830 -1,
1831 3
1832 );
1833
1834 add_action(
1835 'docketcache/action/preload/objectcache',
1836 function () {
1837 if ($this->cf()->is_dctrue('WPCLI')) {
1838 return;
1839 }
1840
1841 // lock opcache reset
1842 $this->co()->setlock('preload_lock_opcache_reset', time() + 20);
1843
1844 // warmup: see after_delay
1845 if ($this->cf()->is_dcfalse('PRELOAD')) {
1846 add_action(
1847 'shutdown',
1848 function () {
1849 if ($this->co()->lockproc('preload', time() + 3600)) {
1850 return false;
1851 }
1852 //wp_load_alloptions();
1853 wp_count_comments(0);
1854 wp_count_posts();
1855 @Crawler::fetch_home(['blocking' => true]);
1856
1857 $this->co()->lockreset('preload');
1858 },
1859 \PHP_INT_MAX
1860 );
1861
1862 return;
1863 }
1864
1865 // preload
1866 add_action(
1867 'shutdown',
1868 function () {
1869 if ($this->co()->lockproc('preload', time() + 3600)) {
1870 return false;
1871 }
1872
1873 wp_load_alloptions();
1874 wp_count_comments(0);
1875 wp_count_posts();
1876
1877 @Crawler::fetch_home(['blocking' => true]);
1878
1879 $preload_admin = [
1880 'index.php',
1881 'options-general.php',
1882 'options-writing.php',
1883 'options-reading.php',
1884 'options-discussion.php',
1885 'options-media.php',
1886 'options-permalink.php',
1887 'edit-comments.php',
1888 'profile.php',
1889 'users.php',
1890 'upload.php',
1891 'plugins.php',
1892 'edit.php',
1893 'edit-tags.php?taxonomy=category',
1894 'edit-tags.php?taxonomy=post_tag',
1895 'edit.php?post_type=page',
1896 'post-new.php?post_type=page',
1897 'themes.php',
1898 'widgets.php',
1899 'nav-menus.php',
1900 'tools.php',
1901 'import.php',
1902 'export.php',
1903 'site-health.php',
1904 'update-core.php',
1905 ];
1906
1907 $preload_network = [
1908 'index.php',
1909 'update-core.php',
1910 'sites.php',
1911 'users.php',
1912 'themes.php',
1913 'plugins.php',
1914 'settings.php',
1915 ];
1916
1917 if ($this->cf()->is_dcarray('PRELOAD_ADMIN')) {
1918 $preload_admin = $this->cf()->dcvalue('PRELOAD_ADMIN');
1919 }
1920
1921 if ($this->cf()->is_dcarray('PRELOAD_NETWORK')) {
1922 $preload_network = $this->cf()->dcvalue('PRELOAD_NETWORK');
1923 }
1924
1925 if (\is_array($preload_admin) && !empty($preload_admin)) {
1926 foreach ($preload_admin as $path) {
1927 $url = admin_url('/'.$path);
1928 @Crawler::fetch_admin($url, ['blocking' => true]);
1929 usleep(500000);
1930 }
1931 }
1932
1933 if (is_multisite() && \is_array($preload_network) && !empty($preload_network)) {
1934 foreach ($preload_network as $path) {
1935 $url = network_admin_url('/'.$path);
1936 @Crawler::fetch_admin($url, ['blocking' => true]);
1937 usleep(500000);
1938 }
1939 }
1940
1941 $this->co()->lockreset('preload');
1942 },
1943 \PHP_INT_MAX
1944 );
1945 }
1946 );
1947
1948 add_action(
1949 'docketcache/action/countcachesize',
1950 function () {
1951 if ($this->co()->lockproc('doing_countcachesize', time() + $this->get_max_execution_time())) {
1952 return;
1953 }
1954
1955 $cache_stats = $this->cache_size($this->cache_path);
1956 $this->co()->save_part($cache_stats, 'cachestats');
1957
1958 $this->co()->lockreset('doing_countcachesize');
1959 },
1960 \PHP_INT_MAX
1961 );
1962
1963 add_action(
1964 'docketcache/action/flushcache/object',
1965 function () {
1966 $this->flush_cache(true);
1967 }
1968 );
1969
1970 // page action
1971 if (class_exists('Nawawi\\DocketCache\\ReqAction')) {
1972 ( new ReqAction($this) )->register();
1973 }
1974 }
1975
1976 /**
1977 * register_tweaks.
1978 */
1979 private function register_tweaks()
1980 {
1981 if (\defined('AUTOSAVE_INTERVAL') && false === AUTOSAVE_INTERVAL) {
1982 add_action(
1983 'init',
1984 function () {
1985 wp_deregister_script('autosave');
1986 },
1987 \PHP_INT_MAX
1988 );
1989 }
1990
1991 if (class_exists('Nawawi\\DocketCache\\Tweaks')) {
1992 $tweaks = new Tweaks();
1993
1994 if ($this->cf()->is_dctrue('OPTWPQUERY')) {
1995 $tweaks->wpquery();
1996 }
1997
1998 if ($this->cf()->is_dctrue('WOOTWEAKS')) {
1999 $tweaks->woocommerce_misc();
2000 }
2001
2002 if ($this->cf()->is_dctrue('WOOADMINOFF')) {
2003 $tweaks->woocommerce_admin_disabled();
2004 }
2005
2006 if ($this->cf()->is_dctrue('WOOWPDASHBOARDOFF')) {
2007 $tweaks->woocommerce_dashboard_status_remove();
2008 }
2009
2010 if ($this->cf()->is_dctrue('WOOWIDGETOFF')) {
2011 $tweaks->woocommerce_widget_remove();
2012 }
2013
2014 if ($this->cf()->is_dctrue('WOOCARTFRAGSOFF')) {
2015 $tweaks->woocommerce_cart_fragments_remove();
2016 }
2017
2018 if ($this->cf()->is_dctrue('WOOADDTOCHARTCRAWLING')) {
2019 $tweaks->woocommerce_crawling_addtochart_links();
2020 }
2021
2022 if ($this->cf()->is_dctrue('WOOEXTENSIONPAGEOFF')) {
2023 $tweaks->woocommerce_extensionpage_remove();
2024 }
2025
2026 if ($this->cf()->is_dctrue('MISC_TWEAKS')) {
2027 $tweaks->misc();
2028 }
2029
2030 if ($this->cf()->is_dctrue('HEADERJUNK')) {
2031 $tweaks->headerjunk();
2032 }
2033
2034 if ($this->cf()->is_dctrue('PINGBACK')) {
2035 $tweaks->pingback();
2036 }
2037
2038 if ($this->cf()->is_dctrue('WPEMOJI')) {
2039 $tweaks->wpemoji();
2040 }
2041
2042 if ($this->cf()->is_dctrue('WPFEED')) {
2043 $tweaks->wpfeed();
2044 }
2045
2046 if ($this->cf()->is_dctrue('WPEMBED')) {
2047 $tweaks->wpembed();
2048 }
2049
2050 if ($this->cf()->is_dctrue('WPLAZYLOAD')) {
2051 $tweaks->wplazyload();
2052 }
2053
2054 if ($this->cf()->is_dctrue('WPSITEMAP')) {
2055 $tweaks->wpsitemap();
2056 }
2057
2058 if ($this->cf()->is_dctrue('WPAPPPASSWORD')) {
2059 $tweaks->wpapppassword();
2060 }
2061
2062 if ($this->cf()->is_dctrue('WPDASHBOARDNEWS')) {
2063 $tweaks->wpdashboardnews();
2064 }
2065
2066 if ($this->cf()->is_dctrue('WPBROWSEHAPPY')) {
2067 $tweaks->wpbrowsehappy();
2068 }
2069
2070 if ($this->cf()->is_dctrue('WPSERVEHAPPY')) {
2071 $tweaks->wpservehappy();
2072 }
2073
2074 if ($this->cf()->is_dctrue('LIMITHTTPREQUEST')) {
2075 $tweaks->limit_http_request();
2076 }
2077
2078 if (version_compare($GLOBALS['wp_version'], '5.8', '<')) {
2079 if ($this->cf()->is_dctrue('HTTPHEADERSEXPECT')) {
2080 $tweaks->http_headers_expect();
2081 }
2082 }
2083
2084 if ($this->cf()->is_dctrue('POSTMISSEDSCHEDULE')) {
2085 add_action(
2086 'shutdown',
2087 function () use ($tweaks) {
2088 if ($this->co()->lockproc('post_missed_schedule', time() + 180)) {
2089 return false;
2090 }
2091 $this->close_buffer();
2092 $tweaks->post_missed_schedule();
2093 $this->co()->lockreset('post_missed_schedule');
2094 },
2095 \PHP_INT_MAX
2096 );
2097 }
2098
2099 if ($this->cf()->is_dctrue('CACHEHTTPRESPONSE')) {
2100 $tweaks->cache_http_response();
2101 }
2102 }
2103
2104 // only if dropin exists
2105 if (wp_using_ext_object_cache()) {
2106 // wp_cache: advanced cache post
2107 if ($this->cf()->is_dctrue('ADVCPOST') && class_exists('Nawawi\\DocketCache\\PostCache')) {
2108 ( new PostCache() )->register();
2109 }
2110
2111 // wp_cache: translation mo file cache
2112 if ($this->cf()->is_dctrue('MOCACHE') && class_exists('Nawawi\\DocketCache\\MoCache')) {
2113 add_filter(
2114 'override_load_textdomain',
2115 function ($plugin_override, $domain, $mofile) {
2116 if (!@is_file($mofile) || !@is_readable($mofile) || !isset($GLOBALS['l10n'])) {
2117 return false;
2118 }
2119
2120 $l10n = $GLOBALS['l10n'];
2121 $upstream = empty($l10n[$domain]) ? null : $l10n[$domain];
2122 $mo = new MoCache($mofile, $domain, $upstream);
2123 $l10n[$domain] = $mo;
2124
2125 $GLOBALS['l10n'] = $l10n;
2126
2127 return true;
2128 },
2129 \PHP_INT_MAX,
2130 3
2131 );
2132 }
2133
2134 // wp_cache: menu cache
2135 if ($this->cf()->is_dctrue('MENUCACHE') && class_exists('Nawawi\\DocketCache\\MenuCache')) {
2136 ( new MenuCache() )->register();
2137 }
2138 }
2139
2140 // optimize term count
2141 if ($this->cf()->is_dctrue('OPTERMCOUNT') && class_exists('Nawawi\\DocketCache\\TermCount')) {
2142 ( new TermCount() )->register();
2143 }
2144 }
2145
2146 /**
2147 * register_cronjob.
2148 */
2149 private function register_cronjob()
2150 {
2151 ( new Event($this) )->register();
2152 }
2153
2154 /**
2155 * unregister_cronjob.
2156 */
2157 private function unregister_cronjob()
2158 {
2159 ( new Event($this) )->unregister();
2160 }
2161
2162 private function register_cli()
2163 {
2164 if ($this->cf()->is_dctrue('WPCLI') && $this->cf()->is_false('DocketCache_CLI')) {
2165 \define('DocketCache_CLI', true);
2166 $cli = new Command($this);
2167
2168 \WP_CLI::add_command('cache dropin:update', [$cli, 'dropino_update']);
2169 \WP_CLI::add_command('cache dropin:enable', [$cli, 'dropino_enable']);
2170 \WP_CLI::add_command('cache dropin:disable', [$cli, 'dropino_disable']);
2171 \WP_CLI::add_command('cache run:gc', [$cli, 'run_gc']);
2172 \WP_CLI::add_command('cache run:cron', [$cli, 'run_cron']);
2173 \WP_CLI::add_command('cache run:stats', [$cli, 'run_stats']);
2174 \WP_CLI::add_command('cache reset:lock', [$cli, 'reset_lock']);
2175 \WP_CLI::add_command('cache reset:cron', [$cli, 'reset_cron']);
2176
2177 if ($this->cf()->is_dctrue('ADVCPOST')) {
2178 \WP_CLI::add_command('cache flush:advcpost', [$cli, 'flush_advcpost']);
2179 }
2180
2181 if ($this->cf()->is_dctrue('PRECACHE')) {
2182 \WP_CLI::add_command('cache flush:precache', [$cli, 'flush_precache']);
2183 }
2184
2185 if ($this->cf()->is_dctrue('MENUCACHE')) {
2186 \WP_CLI::add_command('cache flush:menucache', [$cli, 'flush_menucache']);
2187 }
2188
2189 if ($this->cf()->is_dctrue('MOCACHE')) {
2190 \WP_CLI::add_command('cache flush:mocache', [$cli, 'flush_mocache']);
2191 }
2192
2193 \WP_CLI::add_command('cache flush:transient', [$cli, 'flush_transient']);
2194 \WP_CLI::add_command('cache flush', [$cli, 'flush_cache']);
2195 \WP_CLI::add_command('cache runtime:install', [$cli, 'runtime_install']);
2196 \WP_CLI::add_command('cache runtime:remove', [$cli, 'runtime_remove']);
2197 \WP_CLI::add_command('cache status', [$cli, 'status']);
2198 \WP_CLI::add_command('cache type', [$cli, 'type']);
2199 nwdcx_runaction('docketcache/init/cli', $this, $cli);
2200 }
2201 }
2202
2203 /**
2204 * register.
2205 */
2206 public function register()
2207 {
2208 if (!$this->compat_notice()) {
2209 return;
2210 }
2211 $this->register_plugin_hooks();
2212 $this->register_admin_hooks();
2213 $this->register_tweaks();
2214 $this->register_cronjob();
2215 $this->register_cli();
2216
2217 nwdcx_runaction('docketcache/init', $this);
2218 }
2219 }
2220