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

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

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