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

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

695 lines 22.1 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 Event
16 {
17 private $pt;
18 private $is_optimizedb;
19 private $max_execution_time = 0;
20 private $wp_start_timestamp = 0;
21
22 public function __construct(Plugin $pt)
23 {
24 $this->pt = $pt;
25 $this->is_optimizedb = false;
26 $this->wp_start_timestamp = \defined('WP_START_TIMESTAMP') ? WP_START_TIMESTAMP : microtime(true);
27 $this->max_execution_time = $this->pt->get_max_execution_time();
28 }
29
30 /**
31 * register.
32 */
33 public function register()
34 {
35 // global
36 add_filter('docketcache/filter/garbagecollector', [$this, 'garbage_collector']);
37
38 add_filter(
39 'cron_schedules',
40 function ($schedules) {
41 $schedules['halfhour'] = [
42 'interval' => 30 * MINUTE_IN_SECONDS,
43 'display' => esc_html__('Every 30 Minutes', 'docket-cache'),
44 ];
45
46 if (empty($schedules['hourly'])) {
47 $schedules['hourly'] = [
48 'interval' => HOUR_IN_SECONDS,
49 'display' => esc_html__('Once Hourly', 'docket-cache'),
50 ];
51 }
52
53 if (empty($schedules['monthly'])) {
54 $schedules['monthly'] = [
55 'interval' => MONTH_IN_SECONDS,
56 'display' => esc_html__('Once Monthly', 'docket-cache'),
57 ];
58 }
59
60 $schedules['docketcache_gc_schedule'] = [
61 'interval' => 5 * MINUTE_IN_SECONDS,
62 'display' => esc_html__('Every 5 Minutes', 'docket-cache'),
63 ];
64
65 $schedules['docketcache_checkversion_schedule'] = [
66 'interval' => 15 * DAY_IN_SECONDS,
67 'display' => esc_html__('Every 15 Days', 'docket-cache'),
68 ];
69
70 return $schedules;
71 },
72 \PHP_INT_MAX
73 );
74
75 add_action(
76 'plugins_loaded',
77 function () {
78 // 19092020: standardize. rename hooks
79 foreach (['docket_cache_gc', 'docket_cache_optimizedb', 'docket_cache_monitor'] as $hx) {
80 if (false !== wp_get_scheduled_event($hx)) {
81 wp_clear_scheduled_hook($hx);
82 }
83 }
84
85 // gc: always enable
86 add_action('docketcache_gc', [$this, 'garbage_collector']);
87 if (!wp_next_scheduled('docketcache_gc')) {
88 wp_schedule_event(time(), 'docketcache_gc_schedule', 'docketcache_gc');
89 }
90
91 // monitor: always enable
92 add_action('docketcache_watchproc', [$this, 'watchproc']);
93 if (!wp_next_scheduled('docketcache_watchproc')) {
94 wp_schedule_event(time(), 'hourly', 'docketcache_watchproc');
95 }
96
97 // optimize db
98 $cronoptmzdb = $this->pt->cf()->dcvalue('CRONOPTMZDB');
99 if (!empty($cronoptmzdb) && 'never' !== $cronoptmzdb && is_main_site()) {
100 $recurrence = '';
101 switch ($cronoptmzdb) {
102 case 'daily':
103 $recurrence = 'daily';
104 break;
105 case 'weekly':
106 $recurrence = 'weekly';
107 break;
108 case 'monthly':
109 $recurrence = 'monthly';
110 break;
111 }
112
113 if (empty($recurrence)) {
114 wp_clear_scheduled_hook('docketcache_optimizedb');
115 } else {
116 $this->is_optimizedb = true;
117 add_action('docketcache_optimizedb', [$this, 'optimizedb']);
118
119 if (!wp_next_scheduled('docketcache_optimizedb')) {
120 wp_schedule_event(time(), $recurrence, 'docketcache_optimizedb');
121 }
122 }
123 } else {
124 if (wp_get_schedule('docketcache_optimizedb')) {
125 wp_clear_scheduled_hook('docketcache_optimizedb');
126 }
127 }
128
129 // check version
130 if ($this->pt->cf()->is_dctrue('CHECKVERSION')) {
131 // 06102020: reset old schedule
132 $check = wp_get_scheduled_event('docketcache_checkversion');
133 if (\is_object($check) && 'docketcache_checkversion_schedule' !== $check->schedule) {
134 wp_clear_scheduled_hook('docketcache_checkversion');
135 }
136
137 if (is_main_site() && is_main_network()) {
138 add_action('docketcache_checkversion', [$this, 'checkversion']);
139 if (!wp_next_scheduled('docketcache_checkversion')) {
140 wp_schedule_event(time(), 'docketcache_checkversion_schedule', 'docketcache_checkversion');
141 }
142 }
143 } else {
144 if (wp_get_schedule('docketcache_checkversion')) {
145 wp_clear_scheduled_hook('docketcache_checkversion');
146 }
147 }
148 }
149 );
150 }
151
152 /**
153 * unregister.
154 */
155 public function unregister()
156 {
157 foreach (['docketcache_gc', 'docketcache_optimizedb', 'docketcache_watchproc', 'docketcache_checkversion'] as $hx) {
158 wp_clear_scheduled_hook($hx);
159 }
160 }
161
162 /**
163 * reset,.
164 */
165 public function reset()
166 {
167 $this->unregister();
168 $this->register();
169 }
170
171 /**
172 * monitor.
173 */
174 public function watchproc()
175 {
176 if ($this->pt->co()->lockproc('watchproc', time() + 3600)) {
177 return false;
178 }
179
180 if (!$this->is_optimizedb) {
181 $this->delete_expired_transients_db();
182 }
183
184 //$this->clear_unknown_cron();
185
186 $this->pt->get_cache_stats(true);
187 $this->pt->co()->lockreset('watchproc');
188
189 return true;
190 }
191
192 /**
193 * garbage_collector.
194 */
195 public function garbage_collector($force = false)
196 {
197 static $is_done = false;
198
199 $maxfileo = (int) $this->pt->get_cache_maxfile();
200 $maxfile = $maxfileo;
201
202 if ($maxfileo > 10000) {
203 $maxfile = $maxfileo - 1000;
204 }
205
206 $maxfileo_pre = (int) $this->pt->get_precache_maxfile();
207 $maxfile_pre = $maxfileo_pre;
208
209 if ($maxfileo_pre > 10000) {
210 $maxfile_pre = $maxfileo_pre - 1000;
211 }
212
213 $maxttl0 = (int) $this->pt->get_cache_maxttl();
214 $maxttl = $maxttl0;
215 if (!empty($maxttl)) {
216 $maxttl = time() - $maxttl;
217 }
218
219 $chkmaxdisk = false;
220 $maxsizedisk0 = (int) $this->pt->get_cache_maxsize_disk();
221 $maxsizedisk = $maxsizedisk0;
222 if (!empty($maxsizedisk)) {
223 $maxsizedisk = $maxsizedisk - 1048576;
224
225 if ($maxsizedisk > 1048576) {
226 $chkmaxdisk = true;
227 }
228 }
229
230 $collect = (object) [
231 'cache_maxttl' => $maxttl0,
232 'cache_maxfile' => $maxfileo,
233 'cache_maxdisk' => $maxsizedisk0,
234 'cleanup_maxfile' => 0,
235 'cleanup_precache_maxfile' => 0,
236 'cleanup_maxttl' => 0,
237 'cleanup_expire' => 0,
238 'cleanup_maxdisk' => 0,
239 'cache_file' => 0,
240 'cache_cleanup' => 0,
241 'cache_ignore' => 0,
242 'cleanup_failed' => 0,
243 'cleanup_stalecache' => 0,
244 ];
245
246 clearstatcache();
247 if (!$this->pt->is_docketcachedir($this->pt->cache_path) || @is_file(DOCKET_CACHE_CONTENT_PATH.'/.object-cache-flush.txt')) {
248 return $collect;
249 }
250
251 if ($is_done || $this->pt->co()->lockproc('garbage_collector', time() + $this->max_execution_time + 10)) {
252 return $collect;
253 }
254
255 $stalecache_list = [];
256 if ($this->pt->cf()->is_dctrue('FLUSH_STALECACHE')) {
257 $stalecache_list = wp_cache_get('items', 'docketcache-stalecache');
258 wp_cache_delete('items', 'docketcache-stalecache');
259 }
260
261 $delay = $force ? 650 : 5000;
262 wp_suspend_cache_addition(true);
263
264 $fsizetotal = 0;
265 $fcnt = 0;
266 $pcnt = 0;
267 $slowdown = 0;
268
269 foreach ($this->pt->scanfiles($this->pt->cache_path) as $object) {
270 if ($this->max_execution_time > 0 && (microtime(true) - $this->wp_start_timestamp) > $this->max_execution_time) {
271 break;
272 }
273
274 if ($slowdown > 10) {
275 $slowdown = 0;
276 usleep($delay);
277 }
278
279 ++$slowdown;
280
281 try {
282 if (!$object->isFile()) {
283 ++$collect->cache_ignore;
284 continue;
285 }
286
287 $fx = $object->getPathName();
288 $fn = $object->getFileName();
289 $fs = $object->getSize();
290 $fm = time() + 300;
291 $ft = filemtime($fx);
292
293 $this->pt->remove_non_chunk_cache($this->pt->cache_path, $fx);
294 } catch (\Throwable $e) {
295 nwdcx_throwable(__METHOD__, $e);
296 continue;
297 }
298
299 if ($this->pt->cf()->is_dctrue('DEV') && 'cli' === \PHP_SAPI) {
300 echo 'run-gc: '.$fx."\n";
301 }
302
303 if ($fm >= $ft && (0 === $fs || 'dump_' === substr($fn, 0, 5))) {
304 $this->pt->unlink($fx, true);
305
306 if ($force && @is_file($fx)) {
307 ++$collect->cleanup_failed;
308 }
309 continue;
310 }
311
312 // 032e9f2c5b60- = docketcache-precache-
313 if ($maxfile_pre > 0 && '032e9f2c5b60-' === substr($fn, 0, 13)) {
314 ++$pcnt;
315
316 if ($pcnt > $maxfile_pre) {
317 $this->pt->unlink($fx, true);
318
319 if ($force && @is_file($fx)) {
320 ++$collect->cleanup_failed;
321 }
322
323 ++$collect->cleanup_precache_maxfile;
324 continue;
325 }
326 }
327
328 if ($fcnt >= $maxfile) {
329 // trigger WP_Object_Cache
330 wp_cache_set('numfile', $fcnt, 'docketcache-gc', 60);
331
332 $this->pt->unlink($fx, true);
333
334 if ($force && @is_file($fx)) {
335 ++$collect->cleanup_failed;
336 }
337
338 ++$collect->cleanup_maxfile;
339 continue;
340 }
341
342 $fsizetotal += $fs;
343 if ($chkmaxdisk && $fsizetotal > $maxsizedisk) {
344 $this->pt->unlink($fx, true);
345
346 if ($force && @is_file($fx)) {
347 ++$collect->cleanup_failed;
348 }
349
350 ++$collect->cleanup_maxdisk;
351 continue;
352 }
353
354 $data = $this->pt->cache_get($fx);
355 $is_timeout = false;
356 if (false !== $data) {
357 unset($data['data']);
358
359 $is_timeout = !empty($data['timeout']) && $this->pt->valid_timestamp($data['timeout']) ? true : false;
360 if ($is_timeout) {
361 if ($fm >= (int) $data['timeout']) {
362 $this->pt->unlink($fx, true);
363
364 if ($force && @is_file($fx)) {
365 ++$collect->cleanup_failed;
366 }
367
368 unset($data);
369 ++$collect->cleanup_expire;
370 continue;
371 }
372 } else {
373 if (!empty($data['timestamp']) && $this->pt->valid_timestamp($data['timestamp']) && $maxttl > $data['timestamp']) {
374 $this->pt->unlink($fx, true);
375
376 if ($force && @is_file($fx)) {
377 ++$collect->cleanup_failed;
378 }
379
380 unset($data);
381 ++$collect->cleanup_maxttl;
382 continue;
383 }
384 }
385 }
386
387 // no timeout data or 0
388 if (false === $is_timeout && $maxttl > 0 && $maxttl > $ft) {
389 $this->pt->unlink($fx, true);
390
391 if ($force && @is_file($fx)) {
392 ++$collect->cleanup_failed;
393 }
394
395 ++$collect->cleanup_maxttl;
396 continue;
397 }
398
399 // stalecache
400 if ((!empty($stalecache_list) && \is_array($stalecache_list)) && !empty($data) && !empty($data['key']) && !empty($data['group']) && 'docketcache-stalecache' !== $data['group']) {
401 $collect->cleanup_stalecache += $this->flush_stalecache($fx, $data, $stalecache_list);
402 if ($collect->cleanup_stalecache > 0) {
403 continue;
404 }
405 }
406 unset($data);
407
408 ++$fcnt;
409 ++$collect->cache_file;
410 } // foreach1
411
412 $collect->cache_cleanup = $collect->cleanup_maxttl + $collect->cleanup_expire + $collect->cleanup_maxfile + $collect->cleanup_maxdisk + $collect->cleanup_precache_maxfile + $collect->cleanup_stalecache;
413
414 wp_suspend_cache_addition(false);
415
416 $this->pt->co()->lockreset('garbage_collector');
417 $this->pt->cx()->delay_expire();
418
419 $is_done = true;
420
421 // reset
422 wp_cache_delete('numfile', 'docketcache-gc');
423
424 return $collect;
425 }
426
427 /**
428 * flush_stalecache.
429 */
430 public function flush_stalecache($file, $data, $stalecache_list)
431 {
432 $total = 0;
433
434 if (!is_file($file) || empty($data) || empty($data['key']) || empty($data['group']) || empty($stalecache_list) || !\is_array($stalecache_list)) {
435 return $total;
436 }
437
438 $slowdown = 0;
439 foreach ($stalecache_list as $id => $key) {
440 $do_flush = false;
441
442 if (false !== strpos($data['key'], 'wc_cache_') && 'wc_cache:' === substr($key, 0, 9) && preg_match('@^wc_cache_([0-9\. ]+)_.*@', $data['key'], $mm)) {
443 list($prefix, $group, $usec) = explode(':', $key);
444 if ($usec === $mm[1]) {
445 $do_flush = true;
446 } else {
447 $usec1 = nwdcx_microtimetofloat($usec);
448 $usec2 = nwdcx_microtimetofloat($mm[1]);
449 if ($usec1 > $usec2) {
450 $do_flush = true;
451 }
452 }
453
454 // group = from cache file, key = list key
455 } elseif (false !== strpos($data['group'], 'docketcache-post-') && false !== strpos($key, 'docketcache-post-')) {
456 if ($key === $data['group']) {
457 $do_flush = true;
458 } else {
459 $usec1 = str_replace('docketcache-post-', '', $key);
460 $usec2 = str_replace('docketcache-post-', '', $data['group']);
461 if ($usec1 > $usec2) {
462 $do_flush = true;
463 }
464 }
465 } elseif (false !== strpos($key, 'last_changed:') && @preg_match('@(.*):([a-z0-9]{32}):([0-9\. ]+)$@', $data['key'], $mm)) {
466 list($prefix, $group, $usec) = explode(':', $key);
467 if ($group === $data['group']) {
468 $usec1 = nwdcx_microtimetofloat($usec);
469 $usec2 = nwdcx_microtimetofloat($mm[3]);
470 if ($usec1 > $usec2) {
471 $do_flush = true;
472 }
473 }
474 } elseif (false !== strpos($key, 'after:') && @preg_match('@(.*):([a-z0-9]{32}):([0-9\. ]+)$@', $data['key'], $mm)) {
475 list($prefix, $group, $usec, $abc) = explode(':', $key);
476 if ($group === $data['group'] && $abc === $mm[1]) {
477 $usec1 = nwdcx_microtimetofloat($usec);
478 $usec2 = nwdcx_microtimetofloat($mm[3]);
479 if ($usec1 > $usec2) {
480 $do_flush = true;
481 }
482 }
483 }
484
485 if ($do_flush) {
486 $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
487 // use native unlink since it is a junk file.
488 if (@unlink($file)) {
489 unset($stalecache_list[$id]);
490
491 if ($this->pt->cf()->is_dctrue('DEV') && 'cli' === \PHP_SAPI) {
492 echo 'run-gc:stale-cache: '.$file."\n";
493 }
494
495 ++$total;
496 }
497 nwdcx_suppresserrors($nwdcx_suppresserrors);
498 break; // found and break foreach
499 }
500
501 if ($slowdown > 10) {
502 $slowdown = 0;
503 usleep(5000);
504 }
505
506 ++$slowdown;
507 }
508
509 return $total;
510 }
511
512 /**
513 * optimizedb.
514 */
515 public function optimizedb()
516 {
517 if (!nwdcx_wpdb($wpdb)) {
518 return false;
519 }
520
521 if ($this->pt->co()->lockproc('optimizedb', time() + 3600)) {
522 return false;
523 }
524
525 $suppress = $wpdb->suppress_errors(true);
526
527 @set_time_limit(300);
528 $this->delete_expired_transients_db();
529
530 if (is_main_site() && is_main_network()) {
531 $dbname = $wpdb->dbname;
532 $tables = $wpdb->get_results('SHOW TABLES FROM '.$dbname, ARRAY_A);
533 if (!empty($tables) && \is_array($tables)) {
534 $max_execution_time = $this->max_execution_time;
535 if ($max_execution_time < 300) {
536 $max_execution_time = 300;
537 }
538 foreach ($tables as $table) {
539 $tbl = $table['Tables_in_'.$dbname];
540 $wpdb->query('OPTIMIZE TABLE `'.$tbl.'`');
541
542 if ($this->max_execution_time > 0 && (microtime(true) - $this->wp_start_timestamp) > $this->max_execution_time) {
543 break;
544 }
545 }
546 }
547 unset($tables);
548 }
549
550 $wpdb->suppress_errors($suppress);
551
552 return true;
553 }
554
555 /**
556 * delete_expired_transients_db.
557 */
558 public function delete_expired_transients_db()
559 {
560 if (!wp_using_ext_object_cache()) {
561 return false;
562 }
563
564 if (!nwdcx_wpdb($wpdb)) {
565 return false;
566 }
567
568 if (\function_exists('nwdcx_cleanuptransient')) {
569 nwdcx_cleanuptransient();
570 } elseif (\function_exists('delete_expired_transients')) {
571 delete_expired_transients(true);
572 }
573
574 return true;
575 }
576
577 /**
578 * clear_unknown_cron.
579 */
580 public function clear_unknown_cron()
581 {
582 // let's wp handles it.
583 return;
584
585 if (!wp_using_ext_object_cache()) {
586 return;
587 }
588
589 if (!\function_exists('_get_cron_array')) {
590 return;
591 }
592 $crons = _get_cron_array();
593 if (!empty($crons) && \is_array($crons)) {
594 foreach ($crons as $time => $cron) {
595 foreach ($cron as $hook => $dings) {
596 if (!has_action($hook)) {
597 wp_clear_scheduled_hook($hook);
598 }
599 }
600 }
601 }
602 unset($crons);
603 }
604
605 public function checkversion()
606 {
607 if (!is_main_site()) {
608 return false;
609 }
610
611 $part = 'checkversion';
612
613 if ($this->pt->co()->lockproc($part, time() + 3600)) {
614 return false;
615 }
616
617 $checkdata = $this->pt->co()->get_part($part, true);
618 if (!empty($checkdata) && \is_array($checkdata) && !empty($checkdata['selfcheck'])) {
619 $selfcheck = $checkdata['selfcheck'];
620 if (0 === $this->pt->sanitize_timestamp($selfcheck)) {
621 return false;
622 }
623
624 if ($selfcheck > 0 && $selfcheck > time()) {
625 return false;
626 }
627 }
628
629 $main_site_url = $this->pt->site_url();
630 $site_url = $this->pt->site_url(true);
631 $home_url = $this->pt->site_url(true, true);
632 $stmp = time() + 120;
633 $api_endpoint = $this->pt->api_endpoint.'/'.$part.'?v='.$stmp;
634
635 $args = [
636 'blocking' => true,
637 'body' => [
638 'timestamp' => date('Y-m-d H:i:s T'),
639 'timezone' => wp_timezone_string(),
640 'site' => $site_url,
641 'token' => $this->pt->nw_encrypt($main_site_url, md5($site_url)),
642 'meta' => $this->pt->site_meta(),
643 ],
644 'headers' => [
645 'REFERER' => $home_url,
646 'Cache-Control' => 'no-cache',
647 ],
648 ];
649
650 $results = Crawler::post($api_endpoint, $args);
651
652 $output = [
653 'timestamp' => time(),
654 'endpoint' => $api_endpoint,
655 'request' => [
656 'headers' => $args['headers'],
657 'content' => $args['body'],
658 ],
659 'selfcheck' => time() + 86400,
660 ];
661
662 if (is_wp_error($results)) {
663 $output['error'] = $results->get_error_message();
664 $this->pt->co()->save_part($output, $part);
665
666 return false;
667 }
668
669 $output['response'] = wp_remote_retrieve_body($results);
670 if (!empty($output['response'])) {
671 $output['response'] = json_decode($output['response'], true);
672 if (\JSON_ERROR_NONE === json_last_error()) {
673 if (!empty($output['response']['error'])) {
674 $output['error'] = $output['response']['error'];
675 $this->pt->co()->save_part($output, $part);
676
677 return false;
678 }
679 }
680 }
681
682 $code = (int) wp_remote_retrieve_response_code($results);
683 if ($code > 400) {
684 $output['error'] = $code;
685 $this->pt->co()->save_part($output, $part);
686
687 return false;
688 }
689
690 $this->pt->co()->save_part($output, $part);
691
692 return true;
693 }
694 }
695