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

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

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