PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.4.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.4.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / cache / class-wpo-page-cache.php

class-wpo-page-cache.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.4.0, at cache/class-wpo-page-cache.php

1,500 lines 46.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Page caching functionality
4 *
5 * Acknowledgement: The page cache functionality was loosely based on the simple cache plugin - https://github.com/tlovett1/simple-cache
6 */
7
8 if (!defined('ABSPATH')) die('No direct access allowed');
9
10 /**
11 * Base cache directory, everything else goes under here
12 */
13 if (!defined('WPO_CACHE_DIR')) define('WPO_CACHE_DIR', untrailingslashit(WP_CONTENT_DIR).'/wpo-cache');
14
15 /**
16 * Extensions directory.
17 */
18 if (!defined('WPO_CACHE_EXT_DIR')) define('WPO_CACHE_EXT_DIR', dirname(__FILE__).'/extensions');
19
20 /**
21 * Directory that stores config and related files
22 */
23 if (!defined('WPO_CACHE_CONFIG_DIR')) define('WPO_CACHE_CONFIG_DIR', WPO_CACHE_DIR.'/config');
24
25 /**
26 * Directory that stores the cache, including gzipped files and mobile specific cache
27 */
28 if (!defined('WPO_CACHE_FILES_DIR')) define('WPO_CACHE_FILES_DIR', untrailingslashit(WP_CONTENT_DIR).'/cache/wpo-cache');
29
30 require_once dirname(__FILE__) . '/file-based-page-cache-functions.php';
31
32 if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
33 require_once dirname(__FILE__) . '/php-5.3-functions.php';
34 }
35
36 wpo_cache_load_extensions();
37
38 if (!class_exists('WPO_Page_Cache')) :
39
40 class WPO_Page_Cache {
41
42 /**
43 * Cache config object
44 *
45 * @var mixed
46 */
47 public $config;
48
49 /**
50 * Logger for this class
51 *
52 * @var mixed
53 */
54 public $logger;
55
56 /**
57 * Instance of this class
58 *
59 * @var mixed
60 */
61 public static $instance;
62
63 /**
64 * Store last advanced cache file writing status
65 * If true then last writing finished with error
66 *
67 * @var bool
68 */
69 public $advanced_cache_file_writing_error;
70
71 /**
72 * Store errors
73 *
74 * @var array
75 */
76 private $_errors = array();
77
78 /**
79 * Last advanced cache file content
80 *
81 * @var string
82 */
83 public $advanced_cache_file_content;
84
85 /**
86 * Store the latest advanced-cache.php version required
87 *
88 * @var string
89 */
90 private $_minimum_advanced_cache_file_version = '3.0.17';
91
92 public $should_purge;
93
94 /**
95 * Set everything up here
96 */
97 public function __construct() {
98 $this->config = WPO_Cache_Config::instance();
99 WPO_Cache_Rules::instance();
100 $this->logger = new Updraft_PHP_Logger();
101
102 add_action('activated_plugin', array($this, 'activate_deactivate_plugin'));
103 add_action('deactivate_plugin', array($this, 'activate_deactivate_plugin'));
104 add_action('wpo_purge_old_cache', array($this, 'purge_old'));
105
106 /**
107 * Regenerate config file on cache flush.
108 */
109 add_action('wpo_cache_flush', array($this, 'update_cache_config'));
110 add_action('wpo_cache_flush', array($this, 'delete_cache_size_information'));
111 add_action('update_option_permalink_structure', array($this, 'update_option_permalink_structure'), 10, 3);
112
113 // Add purge cache link to admin bar.
114 add_filter('wpo_cache_admin_bar_menu_items', array($this, 'admin_bar_purge_cache'), 20, 1);
115
116 // Handle single page purge.
117 add_action('wp_loaded', array($this, 'handle_purge_single_page_cache'));
118
119 add_action('admin_init', array($this, 'admin_init'));
120
121 add_action('update_option_gmt_offset', array($this, 'update_gmt_offset_timezone_string_config'), 10, 3);
122 add_action('update_option_timezone_string', array($this, 'update_gmt_offset_timezone_string_config'), 10, 3);
123 add_action('update_option_date_format', array($this, 'update_option_date_format'), 10, 2);
124 add_action('update_option_time_format', array($this, 'update_option_time_format'), 10, 2);
125
126 $this->check_compatibility_issues();
127
128 add_filter('cron_schedules', array($this, 'cron_schedules'));
129 add_action('wpo_save_images_settings', array($this, 'update_webp_images_option'));
130 }
131
132 /**
133 * Activate cron job when the cache is enabled for deleting expired cache files
134 */
135 public function cron_activate() {
136 $page_cache_length = $this->config->get_option('page_cache_length');
137 if ($this->is_enabled()) {
138 if (!wp_next_scheduled('wpo_purge_old_cache')) {
139 wp_schedule_event(time() + (false === $page_cache_length ? '86400' : $page_cache_length), 'wpo_purge_old_cache', 'wpo_purge_old_cache');
140 }
141 } else {
142 wp_clear_scheduled_hook('wpo_purge_old_cache');
143 }
144 }
145
146 /**
147 * Do required actions on activate/deactivate any plugin.
148 */
149 public function activate_deactivate_plugin() {
150
151 wp_clear_scheduled_hook('wpo_purge_old_cache');
152
153 $this->update_cache_config();
154
155 $this->purge();
156 }
157
158 /**
159 * Check if current user can purge cache.
160 *
161 * @return bool
162 */
163 public function can_purge_cache() {
164 if (!$this->is_enabled()) return false;
165 $required_capability = is_multisite() ? 'manage_network_options' : 'manage_options';
166
167 if (WP_Optimize::is_premium()) {
168 return current_user_can($required_capability) || WP_Optimize_Premium()->can_purge_the_cache();
169 } else {
170 return current_user_can($required_capability);
171 }
172 }
173
174 /**
175 * Add Purge from cache in admin bar.
176 *
177 * @param array $menu_items
178 * @return array
179 */
180 public function admin_bar_purge_cache($menu_items) {
181 global $pagenow;
182 if (!$this->can_purge_cache()) return $menu_items;
183
184 $act_url = remove_query_arg(array('wpo_single_page_cache_purged', 'wpo_all_pages_cache_purged'));
185
186 $cache_size = $this->get_cache_size();
187 $cache_size_info = '<h4>'.__('Page cache', 'wp-optimize').'</h4>';
188 $cache_size_info .= '<span>'.__('Cache size:', 'wp-optimize').' '. WP_Optimize()->format_size($cache_size['size']).' '.sprintf(__('(%d files)', 'wp-optimize'), $cache_size['file_count']).'</span>';
189
190 $menu_items[] = array(
191 'id' => 'wpo_cache_stats',
192 'title' => $cache_size_info,
193 'meta' => array(
194 'class' => 'wpo-cache-stats',
195 ),
196 'parent' => 'wpo_purge_cache',
197 );
198
199 $menu_items[] = array(
200 'id' => 'wpo_purge_all_pages_cache',
201 'title' => __('Purge cache for all pages', 'wp-optimize'),
202 'href' => add_query_arg('_wpo_purge', wp_create_nonce('wpo_purge_all_pages_cache'), $act_url),
203 'meta' => array(
204 'title' => __('Purge cache for all pages', 'wp-optimize'),
205 ),
206 'parent' => 'wpo_purge_cache',
207 );
208
209 if (!is_admin() || 'post.php' == $pagenow) {
210 $menu_items[] = array(
211 'id' => 'wpo_purge_this_page_cache',
212 'title' => __('Purge cache for this page', 'wp-optimize'),
213 'href' => add_query_arg('_wpo_purge', wp_create_nonce('wpo_purge_single_page_cache'), $act_url),
214 'meta' => array(
215 'title' => __('Purge cache for this page', 'wp-optimize'),
216 ),
217 'parent' => 'wpo_purge_cache',
218 );
219 }
220
221 return $menu_items;
222
223 }
224
225 /**
226 * Check if purge single page action sent and purge cache.
227 */
228 public function handle_purge_single_page_cache() {
229
230 if (!$this->can_purge_cache()) return;
231
232 if (isset($_GET['wpo_single_page_cache_purged']) || isset($_GET['wpo_all_pages_cache_purged'])) {
233 if (isset($_GET['wpo_single_page_cache_purged'])) {
234 $notice_function = $_GET['wpo_single_page_cache_purged'] ? 'notice_purge_single_page_cache_success' : 'notice_purge_single_page_cache_error';
235 } else {
236 $notice_function = $_GET['wpo_all_pages_cache_purged'] ? 'notice_purge_all_pages_cache_success' : 'notice_purge_all_pages_cache_error';
237 }
238
239 add_action('admin_notices', array($this, $notice_function));
240
241 return;
242 }
243
244 if (!isset($_GET['_wpo_purge'])) return;
245
246 if (wp_verify_nonce($_GET['_wpo_purge'], 'wpo_purge_single_page_cache')) {
247 $success = false;
248
249 if (is_admin()) {
250 $post = isset($_GET['post']) ? (int) $_GET['post'] : 0;
251 if ($post > 0) {
252 $success = self::delete_single_post_cache($post);
253 }
254 } else {
255 $success = self::delete_cache_by_url(wpo_current_url());
256 }
257
258 // remove nonce from url and reload page.
259 wp_redirect(add_query_arg('wpo_single_page_cache_purged', $success, remove_query_arg('_wpo_purge')));
260 exit;
261
262 } elseif (wp_verify_nonce($_GET['_wpo_purge'], 'wpo_purge_all_pages_cache')) {
263 $success = self::purge();
264
265 // remove nonce from url and reload page.
266 wp_redirect(add_query_arg('wpo_all_pages_cache_purged', $success, remove_query_arg('_wpo_purge')));
267 exit;
268 }
269 }
270
271 /**
272 * Show notification when page cache purged successfully.
273 */
274 public function notice_purge_single_page_cache_success() {
275 $this->show_notice(__('The page cache was successfully purged.', 'wp-optimize'), 'success');
276 }
277
278 /**
279 * Show notification when page cache wasn't purged.
280 */
281 public function notice_purge_single_page_cache_error() {
282 $this->show_notice(__('The page cache was not purged.', 'wp-optimize'), 'error');
283 }
284
285 /**
286 * Show notification when all pages cache purged successfully.
287 */
288 public function notice_purge_all_pages_cache_success() {
289 $this->show_notice(__('The page cache was successfully purged.', 'wp-optimize'), 'success');
290 }
291
292 /**
293 * Show notification when all pages cache wasn't purged.
294 */
295 public function notice_purge_all_pages_cache_error() {
296 $this->show_notice(__('The page cache was not purged.', 'wp-optimize'), 'error');
297 }
298
299 /**
300 * Show notification in WordPress admin.
301 *
302 * @param string $message HTML (no further escaping is performed)
303 * @param string $type error, warning, success, or info
304 */
305 public function show_notice($message, $type) {
306 global $current_screen;
307
308 if ($current_screen && is_callable(array($current_screen, 'is_block_editor')) && $current_screen->is_block_editor()) : ?>
309 <script>
310 window.addEventListener('load', function() {
311 (function(wp) {
312 if (window.wp && wp.hasOwnProperty('data') && 'function' == typeof wp.data.dispatch) {
313 wp.data.dispatch('core/notices').createNotice(
314 '<?php echo $type; ?>',
315 '<?php echo $message; ?>',
316 {
317 isDismissible: true,
318 }
319 );
320 }
321 })(window.wp);
322 });
323 </script>
324 <?php else : ?>
325 <div class="notice wpo-notice notice-<?php echo $type; ?> is-dismissible">
326 <p><?php echo $message; ?></p>
327 </div>
328 <?php
329 endif;
330 }
331
332 /**
333 * Enables page cache
334 *
335 * @param bool $force_enable - Force regenerating everything. E.g. we want to do that when saving the settings
336 *
337 * @return WP_Error|bool - true on success, error otherwise
338 */
339 public function enable($force_enable = false) {
340 static $already_ran_enable = false;
341
342 if ($already_ran_enable) return $already_ran_enable;
343
344 $folders_created = $this->create_folders();
345 if (is_wp_error($folders_created)) {
346 $already_ran_enable = $folders_created;
347 return $already_ran_enable;
348 }
349
350 if (!file_exists($this->config->get_config_file_path())) {
351 $result = $this->update_cache_config();
352 if (is_wp_error($result)) return $result;
353 }
354
355 // if WPO_ADVANCED_CACHE isn't set, or environment doesn't contain the right constant, force regeneration
356 if (!defined('WPO_ADVANCED_CACHE') || !defined('WP_CACHE')) {
357 $force_enable = true;
358 }
359
360 $this->maybe_regenerate_cache_config_file();
361
362 if (!$force_enable) {
363 $already_ran_enable = true;
364 return true;
365 }
366
367 if (!$this->write_advanced_cache() && version_compare($this->get_advanced_cache_version(), $this->_minimum_advanced_cache_file_version, '<')) {
368 $message = sprintf("The request to write the file %s failed. ", htmlspecialchars($this->get_advanced_cache_filename()));
369 $message .= ' '.__('Please check file and directory permissions on the file paths up to this point, and your PHP error log.', 'wp-optimize');
370
371 if (!defined('WP_CLI') || !WP_CLI) {
372 $message .= "\n\n1. ".sprintf(__('Please navigate, via FTP, to the folder - %s', 'wp-optimize'), htmlspecialchars(dirname($this->get_advanced_cache_filename())));
373 $message .= "\n2. ".__('Edit or create a file with the name advanced-cache.php', 'wp-optimize');
374 $message .= "\n3. ".__('Copy and paste the following lines into the file:', 'wp-optimize');
375 }
376
377 $already_ran_enable = new WP_Error("write_advanced_cache", $message);
378 return $already_ran_enable;
379 }
380
381 if (!$this->write_wp_config(true)) {
382 $already_ran_enable = new WP_Error("write_wp_config", "Could not turn on the WP_CACHE constant in wp-config.php. Check your permissions.");
383 return $already_ran_enable;
384 }
385
386 if (!$this->verify_cache()) {
387 $errors = $this->get_errors();
388 $already_ran_enable = new WP_Error("verify_cache", "Could not verify if the cache was enabled: \n".implode("\n- ", $errors));
389 return $already_ran_enable;
390 }
391
392 $already_ran_enable = true;
393
394 return true;
395 }
396
397 /**
398 * Disables page cache
399 *
400 * @return bool - true on success, false otherwise
401 */
402 public function disable() {
403 $ret = true;
404
405 $advanced_cache_file = $this->get_advanced_cache_filename();
406
407 // N.B. The only use of WP_CACHE in WP core is to include('advanced-cache.php') (and run a function if it's then defined); so, if the decision to leave it enable is, for some unexpected reason, technically incorrect, it still can't cause a problem.
408 $disabled_wp_config = $this->write_wp_config(false);
409 if (!$disabled_wp_config) {
410 $plugin_basename = basename(WPO_PLUGIN_MAIN_PATH);
411 $action = "deactivate_".$plugin_basename."/wp-optimize.php";
412 if (current_action() === $action) {
413 $cache_config = WPO_Cache_Config::instance()->config;
414 $cache_config['enable_page_caching'] = false;
415 WPO_Cache_Config::instance()->update($cache_config, true);
416 }
417 $this->log("Could not turn off the WP_CACHE constant in wp-config.php");
418 $this->add_warning('error_disabling', __('Could not turn off the WP_CACHE constant in wp-config.php', 'wp-optimize'));
419 }
420
421 $disabled_advanced_cache = true;
422 // First try to remove (so that it doesn't look to any other plugin like the file is already 'claimed')
423 // We only touch advanched-cache.php and wp-config.php if it appears that we were in control of advanced-cache.php
424 if (!file_exists($advanced_cache_file) || false !== strpos(file_get_contents($advanced_cache_file), 'WP-Optimize advanced-cache.php')) {
425 if (file_exists($advanced_cache_file) && (!unlink($advanced_cache_file) && false === file_put_contents($advanced_cache_file, "<?php\n// WP-Optimize: page cache disabled"))) {
426 $disabled_advanced_cache = false;
427 $this->log("The request to the filesystem to remove or empty advanced-cache.php failed");
428 $this->add_warning('error_disabling', __('The request to the filesystem to remove or empty advanced-cache.php failed', 'wp-optimize'));
429 }
430 }
431
432 // If both actions failed, the cache wasn't disabled. So we send an error. If only one succeeds, it will still be disabled.
433 if (!$disabled_wp_config && !$disabled_advanced_cache) {
434 $ret = new WP_Error('error_disabling_cache', __('The page caching could not be disabled: the WP_CACHE constant could not be removed from wp-config.php and the request to the filesystem to remove or empty advanced-cache.php failed.', 'wp-optimize'));
435 }
436
437 // Delete cache to avoid stale cache on next activation
438 $this->purge();
439
440 // Unschedule cron job to purge old cache
441 wp_clear_scheduled_hook('wpo_purge_old_cache');
442
443 return $ret;
444 }
445
446
447 /**
448 * Purges the cache
449 *
450 * @return bool - true on success, false otherwise
451 */
452 public function purge() {
453 /**
454 * Filters whether activating / deactivating a plugin will purge the cache.
455 */
456 $this->should_purge = apply_filters('wpo_purge_page_cache_on_activate_deactivate_plugin', true);
457 $purge_actions = array(
458 'deactivate_' . plugin_basename(WPO_PLUGIN_MAIN_PATH . 'wp-optimize.php'),
459 'deactivate_plugin',
460 'activated_plugin',
461 );
462 $is_deactivation_action = in_array(current_action(), $purge_actions, true);
463 if ($is_deactivation_action && !$this->should_purge) return false;
464
465 if (!self::delete(WPO_CACHE_FILES_DIR)) {
466 $this->log("The request to the filesystem to delete the cache failed");
467 return false;
468 }
469
470 /**
471 * Fires after purging the cache
472 */
473 do_action('wpo_cache_flush');
474
475 return true;
476 }
477
478 /**
479 * Purge cache files older than cache life span
480 *
481 * @return array Log messages.
482 */
483 public function purge_old() {
484 $log = array();
485 $page_cache_length = $this->config->get_option('page_cache_length');
486 if (0 === $page_cache_length) return $log;
487 $expires = time() - $page_cache_length;
488 $cache_folder = WPO_CACHE_FILES_DIR . '/' . str_ireplace(array('http://', 'https://'), '', get_site_url());
489 // get all directories that are a direct child of current directory
490 if (is_dir($cache_folder) && is_writable($cache_folder)) {
491 if ($handle = opendir($cache_folder)) {
492 while (false !== ($d = readdir($handle))) {
493 if (0 == strcmp($d, '.') || 0 == strcmp($d, '..')) {
494 continue;
495 }
496
497 if ($this->is_front_page_cache($d)) {
498 $modified_time = (int) filemtime("$cache_folder/$d");
499 if ($modified_time <= $expires) {
500 unlink("$cache_folder/$d");
501 }
502 continue;
503 }
504
505 $dir = $cache_folder.'/'.$d;
506 if (!is_dir($dir)) continue;
507
508 $stat = stat($dir);
509 $modified_time = $stat['mtime'];
510 $log[] = "checking if cache has expired - $d";
511 if ($modified_time <= $expires) {
512 $log[] = "deleting cache in $dir";
513 wpo_delete_files($dir, true);
514 if (file_exists($dir)) rmdir($dir);
515 }
516 }
517 closedir($handle);
518 }
519 }
520 return $log;
521 }
522
523 /**
524 * Finds out given cache file is of front page or not
525 *
526 * @return bool
527 */
528 private function is_front_page_cache($d) {
529 return in_array($d, array('index.html', 'index.html.gz'));
530 }
531
532 /**
533 * Purges the cache
534 *
535 * @return bool - true on success, false otherwise
536 */
537 public function clean_up() {
538
539 $this->disable();
540
541 if (!self::delete(WPO_CACHE_DIR, true)) {
542 $this->log("The request to the filesystem to clean up the cache failed");
543 return false;
544 }
545
546 return true;
547 }
548
549 /**
550 * Check if cache is enabled and working
551 *
552 * @return bool - true on success, false otherwise
553 */
554 public function is_enabled() {
555
556 if (!$this->config->get_option('enable_page_caching')) return false;
557
558 if (!defined('WP_CACHE') || !WP_CACHE) {
559 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
560 $this->log("WP_CACHE constant is not present in wp-config.php");
561 return false;
562 }
563 }
564
565 if (!defined('WPO_ADVANCED_CACHE') || !WPO_ADVANCED_CACHE) {
566 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
567 $this->log("WPO_ADVANCED_CACHE constant is not present in advanced-cache.php");
568 return false;
569 }
570 }
571
572 $config_file = WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename();
573 if (!file_exists($config_file)) {
574 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
575 $this->log("Cache config file $config_file is not present");
576 return false;
577 }
578 }
579
580 return true;
581 }
582
583 /**
584 * Create the folder structure needed for cache to work
585 *
586 * @return bool - true on success, false otherwise
587 */
588 public function create_folders() {
589 $permissions_str = __('Please check your file permissions.', 'wp-optimize');
590
591 if (!is_dir(WPO_CACHE_DIR) && !wp_mkdir_p(WPO_CACHE_DIR)) {
592 return new WP_Error('create_folders', sprintf(__('The request to the filesystem failed: unable to create directory %s.', 'wp-optimize'), str_ireplace(ABSPATH, '', WPO_CACHE_DIR)) .' '. $permissions_str);
593 }
594
595 if (!is_dir(WPO_CACHE_CONFIG_DIR) && !wp_mkdir_p(WPO_CACHE_CONFIG_DIR)) {
596 return new WP_Error('create_folders', sprintf(__('The request to the filesystem failed: unable to create directory %s.', 'wp-optimize'), str_ireplace(ABSPATH, '', WPO_CACHE_CONFIG_DIR)) .' '. $permissions_str);
597 }
598
599 if (!is_dir(WPO_CACHE_FILES_DIR)) {
600 if (!wp_mkdir_p(WPO_CACHE_FILES_DIR)) {
601 return new WP_Error('create_folders', sprintf(__('The request to the filesystem failed: unable to create directory %s.', 'wp-optimize'), str_ireplace(ABSPATH, '', WPO_CACHE_FILES_DIR)) .' '. $permissions_str);
602 } else {
603 wpo_disable_cache_directories_viewing();
604 }
605 }
606
607 return true;
608 }
609
610 /**
611 * Get advanced-cache.php file name with full path.
612 *
613 * @return string
614 */
615 public function get_advanced_cache_filename() {
616 return untrailingslashit(WP_CONTENT_DIR) . '/advanced-cache.php';
617 }
618
619 /**
620 * Writes advanced-cache.php
621 *
622 * @param boolean $update_required - Whether the update is required or not.
623 * @return bool
624 */
625 private function write_advanced_cache($update_required = false) {
626 $config_file_basename = $this->config->get_cache_config_filename();
627 $cache_file_basename = untrailingslashit(plugin_dir_path(__FILE__));
628 $plugin_basename = basename(WPO_PLUGIN_MAIN_PATH);
629 $cache_path = '/wpo-cache';
630 $cache_files_path = '/cache/wpo-cache';
631 $cache_extensions_path = WPO_CACHE_EXT_DIR;
632 $wpo_version = WPO_VERSION;
633 $wpo_home_url = trailingslashit(home_url());
634 $abspath = ABSPATH;
635
636 // CS does not like heredoc
637 // phpcs:disable
638 $this->advanced_cache_file_content = <<<EOF
639 <?php
640
641 if (!defined('ABSPATH')) die('No direct access allowed');
642
643 // WP-Optimize advanced-cache.php (written by version: $wpo_version) (homeurl: $wpo_home_url) (abspath: $abspath) (do not change this line, it is used for correctness checks)
644
645 if (!defined('WPO_ADVANCED_CACHE')) define('WPO_ADVANCED_CACHE', true);
646
647 \$possible_plugin_locations = array(
648 defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR.'/$plugin_basename/cache' : false,
649 defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR.'/plugins/$plugin_basename/cache' : false,
650 dirname(__FILE__).'/plugins/$plugin_basename/cache',
651 '$cache_file_basename',
652 );
653
654 \$plugin_location = false;
655
656 foreach (\$possible_plugin_locations as \$possible_location) {
657 if (false !== \$possible_location && @file_exists(\$possible_location.'/file-based-page-cache.php')) {
658 \$plugin_location = \$possible_location;
659 break;
660 }
661 }
662
663 if (false === \$plugin_location) {
664 if (!defined('WPO_PLUGIN_LOCATION_NOT_FOUND')) define('WPO_PLUGIN_LOCATION_NOT_FOUND', true);
665 \$protocol = \$_SERVER['REQUEST_SCHEME'];
666 \$host = \$_SERVER['HTTP_HOST'];
667 \$request_uri = \$_SERVER['REQUEST_URI'];
668 if (strcasecmp('$wpo_home_url', \$protocol . '://' . \$host . \$request_uri) === 0) {
669 error_log('WP-Optimize: No caching took place, because the plugin location could not be found');
670 }
671 } else {
672 if (!defined('WPO_PLUGIN_LOCATION_NOT_FOUND')) define('WPO_PLUGIN_LOCATION_NOT_FOUND', false);
673 }
674
675 if (is_admin()) { return; }
676
677 if (!defined('WPO_CACHE_DIR')) define('WPO_CACHE_DIR', WP_CONTENT_DIR.'$cache_path');
678 if (!defined('WPO_CACHE_CONFIG_DIR')) define('WPO_CACHE_CONFIG_DIR', WPO_CACHE_DIR.'/config');
679 if (!defined('WPO_CACHE_FILES_DIR')) define('WPO_CACHE_FILES_DIR', WP_CONTENT_DIR.'$cache_files_path');
680 if (false !== \$plugin_location) {
681 if (!defined('WPO_CACHE_EXT_DIR')) define('WPO_CACHE_EXT_DIR', \$plugin_location.'/extensions');
682 } else {
683 if (!defined('WPO_CACHE_EXT_DIR')) define('WPO_CACHE_EXT_DIR', '$cache_extensions_path');
684 }
685
686 if (!@file_exists(WPO_CACHE_CONFIG_DIR . '/$config_file_basename')) { return; }
687
688 \$GLOBALS['wpo_cache_config'] = @json_decode(file_get_contents(WPO_CACHE_CONFIG_DIR . '/$config_file_basename'), true);
689
690 if (empty(\$GLOBALS['wpo_cache_config'])) {
691 include_once(WPO_CACHE_CONFIG_DIR . '/$config_file_basename');
692 }
693
694 if (empty(\$GLOBALS['wpo_cache_config'])) {
695 error_log('WP-Optimize: Caching failed because the configuration data could not be loaded from the config file.');
696 return;
697 }
698
699 if (empty(\$GLOBALS['wpo_cache_config']['enable_page_caching'])) { return; }
700
701 if (false !== \$plugin_location) { include_once(\$plugin_location.'/file-based-page-cache.php'); }
702
703 EOF;
704
705 // phpcs:enable
706 $advanced_cache_filename = $this->get_advanced_cache_filename();
707
708 // If the file content is already up to date, success
709 if (is_file($advanced_cache_filename) && file_get_contents($advanced_cache_filename) === $this->advanced_cache_file_content) {
710 $this->advanced_cache_file_writing_error = false;
711 return true;
712 }
713
714 // check if we can't write the advanced cache file
715 // case 1: the directory is read-only and the file doesn't exist
716 if (!is_file($advanced_cache_filename) && !is_writable(dirname($advanced_cache_filename))) {
717 $this->advanced_cache_file_writing_error = true;
718 return false;
719 }
720
721 // case 2: the file already exists but it's read-only
722 if (is_file($advanced_cache_filename) && !is_writable($advanced_cache_filename)) {
723 if (version_compare($this->get_advanced_cache_version(), $this->_minimum_advanced_cache_file_version, '<') || $update_required) {
724 $this->advanced_cache_file_writing_error = true;
725 return false;
726 } else {
727 $this->advanced_cache_file_writing_error = false;
728 return true;
729 }
730 }
731
732 if (!file_put_contents($this->get_advanced_cache_filename(), $this->advanced_cache_file_content)) {
733 $this->advanced_cache_file_writing_error = true;
734 return false;
735 }
736
737 $this->advanced_cache_file_writing_error = false;
738 return true;
739 }
740
741 /**
742 * Update advanced cache version if needed.
743 */
744 public function maybe_update_advanced_cache() {
745
746 if (!$this->is_enabled()) return;
747
748 if (!defined('WPO_PLUGIN_LOCATION_NOT_FOUND') || (defined('WPO_PLUGIN_LOCATION_NOT_FOUND') && true === WPO_PLUGIN_LOCATION_NOT_FOUND)) {
749 if (!$this->write_advanced_cache(true)) {
750 add_action('admin_notices', array($this, 'show_admin_notice_advanced_cache'));
751 }
752 }
753
754 // from 3.0.17 we use more secure way to store cache config files and need update advanced-cache.php
755 // if the site url or folder changed we also update advanced-cache.php
756 $advanced_cache_current_version = $this->get_advanced_cache_version();
757 if ($advanced_cache_current_version && version_compare($advanced_cache_current_version, $this->_minimum_advanced_cache_file_version, '>=') && !$this->is_the_site_url_or_folder_changed()) return;
758
759 if (!$this->write_advanced_cache()) {
760 add_action('admin_notices', array($this, 'notice_advanced_cache_autoupdate_error'));
761 } else {
762 $this->update_cache_config();
763 }
764 }
765
766 /**
767 * Check if the site url or folder doesn't equal to values from advanced-cache.php
768 *
769 * @return bool
770 */
771 private function is_the_site_url_or_folder_changed() {
772 if (!is_file($this->get_advanced_cache_filename())) return false;
773
774 $content = file_get_contents($this->get_advanced_cache_filename());
775
776 if (false === $content) return false;
777
778 if (preg_match('/WP\-Optimize advanced\-cache\.php \(written by version\: (.+)\) (\(homeurl: (.+)\)) (\(abspath: (.+)\)) \(do not change/Ui', $content, $match)) {
779 $wpo_home_url = trailingslashit(home_url());
780 $abspath = ABSPATH;
781 return ($wpo_home_url != $match[3] || $abspath != $match[5]);
782 } elseif (preg_match('/WP\-Optimize advanced\-cache\.php \(written by version\: (.+)\)/Ui', $content, $match)) {
783 return true;
784 }
785
786 return false;
787 }
788
789 /**
790 * Show notification when advanced-cache.php could not be updated.
791 */
792 public function notice_advanced_cache_autoupdate_error() {
793 $this->show_notice(__('The file advanced-cache.php needs to be updated, but the automatic process failed.', 'wp-optimize').
794 ' <a href="'.admin_url('admin.php?page=wpo_cache').'">'.__('Please try to disable and then re-enable the WP-Optimize cache manually.', 'wp-optimize').'</a>', 'error');
795 }
796
797 /**
798 * Get WPO version number from advanced-cache.php file.
799 *
800 * @return bool|mixed
801 */
802 public function get_advanced_cache_version() {
803 if (!is_file($this->get_advanced_cache_filename())) return false;
804
805 $version = false;
806 $content = file_get_contents($this->get_advanced_cache_filename());
807
808 if (preg_match('/WP\-Optimize advanced\-cache\.php \(written by version\: (.+)\)/Ui', $content, $match)) {
809 $version = $match[1];
810 }
811
812 return $version;
813 }
814
815 /**
816 * Set WP_CACHE on or off in wp-config.php
817 *
818 * @param boolean $status value of WP_CACHE.
819 * @return boolean true if the value was set, false otherwise
820 */
821 private function write_wp_config($status = true) {
822 // If we changed the value in wp-config, save it, in case we need to change it again in the same run.
823 static $changed = false;
824
825 if (defined('WP_CACHE') && WP_CACHE === $status && !$changed) {
826 return true;
827 }
828
829 $config_path = $this->_get_wp_config();
830
831 // Couldn't find wp-config.php.
832 if (!$config_path) {
833 return false;
834 }
835
836 $config_file_string = file_get_contents($config_path);
837
838 // Config file is empty. Maybe couldn't read it?
839 if (empty($config_file_string)) {
840 return false;
841 }
842
843 $config_file = preg_split("#(\n|\r\n)#", $config_file_string);
844 $line_key = false;
845
846 foreach ($config_file as $key => $line) {
847 if (!preg_match('/^\s*define\(\s*(\'|")([A-Z_]+)(\'|")(.*)/i', $line, $match)) {
848 continue;
849 }
850
851 if ('WP_CACHE' === $match[2]) {
852 $line_key = $key;
853 }
854 }
855
856 if (false !== $line_key) {
857 unset($config_file[$line_key]);
858 }
859
860
861 if ($status) {
862 array_shift($config_file);
863 array_unshift($config_file, '<?php', "define('WP_CACHE', true); // WP-Optimize Cache");
864 }
865
866 foreach ($config_file as $key => $line) {
867 if ('' === $line) {
868 unset($config_file[$key]);
869 }
870 }
871 if (!file_put_contents($config_path, implode(PHP_EOL, $config_file))) {
872 return false;
873 }
874 $changed = true;
875 return true;
876 }
877
878 /**
879 * Verify we can write to the file system
880 *
881 * @return boolean
882 */
883 private function verify_cache() {
884 if (function_exists('clearstatcache')) {
885 clearstatcache();
886 }
887 $errors = 0;
888
889 // First check wp-config.php.
890 if (!$this->_get_wp_config() && !is_writable($this->_get_wp_config())) {
891 $this->log("Unable to write to or find wp-config.php; please check file/folder permissions");
892 $this->add_warning('verify_cache', __("Unable to write to or find wp-config.php; please check file/folder permissions.", 'wp-optimize'));
893 }
894
895 $advanced_cache_file = untrailingslashit(WP_CONTENT_DIR).'/advanced-cache.php';
896
897 // Now check wp-content. We need to be able to create files of the same user as this file.
898 if ((!file_exists($advanced_cache_file) || false === strpos(file_get_contents($advanced_cache_file), 'WP-Optimize advanced-cache.php')) && !is_writable($advanced_cache_file) && !is_writable(untrailingslashit(WP_CONTENT_DIR))) {
899 $this->log("Unable to write the file advanced-cache.php inside the wp-content folder; please check file/folder permissions");
900 $this->add_error('verify_cache', __("Unable to write the file advanced-cache.php inside the wp-content folder; please check file/folder permissions", 'wp-optimize'));
901 $errors++;
902 }
903
904 if (file_exists(WPO_CACHE_FILES_DIR)) {
905 if (!is_writable(WPO_CACHE_FILES_DIR)) {
906 $this->log("Unable to write inside the cache files folder; please check file/folder permissions");
907 $this->add_warning('verify_cache', sprintf(__("Unable to write inside the cache files folder (%s); please check file/folder permissions (no cache files will be able to be created otherwise)", 'wp-optimize'), WPO_CACHE_FILES_DIR));
908 }
909 }
910
911 if (file_exists(WPO_CACHE_CONFIG_DIR)) {
912 if (!is_writable(WPO_CACHE_CONFIG_DIR)) {
913 $this->log("Unable to write inside the cache configuration folder; please check file/folder permissions");
914 // If the config exists, only send a warning. Otherwise send an error.
915 $type = 'warning';
916 if (!file_exists(WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename())) {
917 $type = 'error';
918 $errors++;
919 }
920 $this->add_error('verify_cache', sprintf(__("Unable to write inside the cache configuration folder (%s); please check file/folder permissions", 'wp-optimize'), WPO_CACHE_CONFIG_DIR), $type);
921 }
922 }
923
924 return !$errors;
925 }
926
927 /**
928 * Update permalink structure in cache config
929 *
930 * @param string $old_value Old value of permalink_structure option
931 * @param string $value New value of permalink_structure option
932 * @param string $option Option name `permalink_structure`
933 */
934 public function update_option_permalink_structure($old_value, $value, $option) {
935 $current_config = $this->config->get();
936 if ($old_value != $value) {
937 $current_config[$option] = $value;
938 $this->config->update($current_config, true);
939 }
940 }
941
942 /**
943 * Update cache config. Used to support 3d party plugins.
944 *
945 * @return {boolean|WP_Error}
946 */
947 public function update_cache_config() {
948 // get current cache settings.
949 $current_config = $this->config->get();
950 // and call update to change if need cookies and query variable names.
951 return $this->config->update($current_config);
952 }
953
954 /**
955 * Delete information about cache size.
956 */
957 public function delete_cache_size_information() {
958 delete_transient('wpo_get_cache_size');
959 }
960
961 /**
962 * Get current cache size.
963 *
964 * @return array
965 */
966 public function get_cache_size() {
967 $cache_size = get_transient('wpo_get_cache_size');
968
969 if (!empty($cache_size)) return $cache_size;
970
971 $infos = $this->get_dir_infos(WPO_CACHE_FILES_DIR);
972 $cache_size = array(
973 'size' => $infos['size'],
974 'file_count' => $infos['file_count']
975 );
976
977 set_transient('wpo_get_cache_size', $cache_size);
978
979 return $cache_size;
980 }
981
982 /**
983 * Fetch directory information.
984 *
985 * @param string $dir
986 * @return array
987 */
988 private function get_dir_infos($dir) {
989 $dir_size = 0;
990 $file_count = 0;
991
992 $handle = is_dir($dir) ? opendir($dir) : false;
993
994 if (false === $handle) {
995 return array('size' => 0, 'file_count' => 0);
996 }
997
998 $file = readdir($handle);
999
1000 while (false !== $file) {
1001
1002 if ('.' != $file && '..' != $file) {
1003 $current_file = $dir.'/'.$file;
1004
1005 if (is_dir($current_file)) {
1006 $sub_dir_infos = $this->get_dir_infos($current_file);
1007 $dir_size += $sub_dir_infos['size'];
1008 $file_count += $sub_dir_infos['file_count'];
1009 } elseif (is_file($current_file)) {
1010 $dir_size += filesize($current_file);
1011 $file_count++;
1012 }
1013 }
1014
1015 $file = readdir($handle);
1016
1017 }
1018
1019 return array('size' => $dir_size, 'file_count' => $file_count);
1020 }
1021
1022 /**
1023 * Returns the path to wp-config
1024 *
1025 * @return string|boolean wp-config.php path.
1026 */
1027 private function _get_wp_config() {
1028
1029 $config_path = false;
1030
1031 foreach (get_included_files() as $filename) {
1032 if (preg_match('/(\\\\|\/)wp-config\.php$/i', $filename)) {
1033 $config_path = $filename;
1034 break;
1035 }
1036 }
1037
1038 // WP-CLI doesn't include wp-config.php that's why we use function from WP-CLI to locate config file.
1039 if (!$config_path && is_callable('wpo_wp_cli_locate_wp_config')) {
1040 $config_path = wpo_wp_cli_locate_wp_config();
1041 }
1042
1043 return $config_path;
1044 }
1045
1046 /**
1047 * Util to delete folders and/or files
1048 *
1049 * @param string $src
1050 * @return boolean
1051 */
1052 public static function delete($src) {
1053
1054 return wpo_delete_files($src);
1055
1056 }
1057
1058 /**
1059 * Delete cached files for specific url.
1060 *
1061 * @param string $url
1062 * @param bool $recursive If true child elements will deleted too
1063 *
1064 * @return bool
1065 */
1066 public static function delete_cache_by_url($url, $recursive = false) {
1067 if (!defined('WPO_CACHE_FILES_DIR') || '' == $url) return;
1068
1069 $path = self::get_full_path_from_url($url);
1070
1071 do_action('wpo_delete_cache_by_url', $url, $recursive);
1072
1073 return wpo_delete_files($path, $recursive);
1074 }
1075
1076 /**
1077 * Delete cached files for single post.
1078 *
1079 * @param integer $post_id The post ID
1080 *
1081 * @return bool
1082 */
1083 public static function delete_single_post_cache($post_id) {
1084 $is_cache_deleted = self::really_delete_single_post_cache($post_id);
1085
1086 do_action('wpo_single_post_cache_deleted', $post_id);
1087
1088 return $is_cache_deleted;
1089 }
1090
1091 /**
1092 * Really delete cached files for single post.
1093 *
1094 * @param integer $post_id The post ID
1095 *
1096 * @return bool
1097 */
1098 public static function really_delete_single_post_cache($post_id) {
1099
1100 if (!defined('WPO_CACHE_FILES_DIR')) return;
1101
1102 $post_url = get_permalink($post_id);
1103
1104 $path = self::get_full_path_from_url($post_url);
1105
1106 // for posts with pagination run purging cache recursively.
1107 $post = get_post($post_id);
1108 $recursive = preg_match('/\<\!--nextpage--\>/', $post->post_content) ? true : false;
1109
1110 do_action('wpo_delete_cache_by_url', $post_url, $recursive);
1111
1112 return wpo_delete_files($path, $recursive);
1113 }
1114
1115 /**
1116 * Delete cached home page files.
1117 */
1118 public static function delete_homepage_cache() {
1119
1120 if (!defined('WPO_CACHE_FILES_DIR')) return;
1121
1122 $homepage_url = get_home_url(get_current_blog_id());
1123
1124 $path = self::get_full_path_from_url($homepage_url);
1125
1126 do_action('wpo_delete_cache_by_url', $homepage_url, false);
1127
1128 wpo_delete_files($path, false);
1129 }
1130
1131 /**
1132 * Delete feed from cache.
1133 */
1134 public static function delete_feed_cache() {
1135 if (!defined('WPO_CACHE_FILES_DIR')) return;
1136
1137 $homepage_url = get_home_url(get_current_blog_id());
1138
1139 $path = self::get_full_path_from_url($homepage_url) . 'feed/';
1140
1141 do_action('wpo_delete_cache_by_url', $path, true);
1142
1143 wpo_delete_files($path, true);
1144 }
1145
1146 /**
1147 * Delete post feed from cache.
1148 */
1149 public static function delete_post_feed_cache($post_id) {
1150 self::really_delete_post_feed_cache($post_id);
1151
1152 do_action('wpo_single_post_feed_cache_deleted', $post_id);
1153 }
1154
1155 /**
1156 * Really delete post feed from cache.
1157 */
1158 public static function really_delete_post_feed_cache($post_id) {
1159 if (!defined('WPO_CACHE_FILES_DIR')) return;
1160
1161 $post_url = get_permalink($post_id);
1162
1163 $path = self::get_full_path_from_url($post_url) . 'feed/';
1164
1165 do_action('wpo_delete_cache_by_url', $path, true);
1166
1167 wpo_delete_files($path, true);
1168 }
1169
1170 /**
1171 * Delete comments feed from cache.
1172 */
1173 public static function delete_comments_feed() {
1174 if (!defined('WPO_CACHE_FILES_DIR')) return;
1175
1176 $comments_feed_url = trailingslashit(get_home_url(get_current_blog_id())) . 'comments/feed/';
1177
1178 $path = self::get_full_path_from_url($comments_feed_url);
1179
1180 do_action('wpo_delete_cache_by_url', $comments_feed_url, true);
1181
1182 wpo_delete_files($path, true);
1183
1184 // delete empty comments dir from the cache
1185 $comments_url = trailingslashit(get_home_url(get_current_blog_id())) . 'comments/';
1186 $path = self::get_full_path_from_url($comments_url);
1187
1188 if (wpo_is_empty_dir($path)) {
1189 wpo_delete_files($path, true);
1190 }
1191 }
1192
1193 /**
1194 * Returns full path to the cache folder by url.
1195 *
1196 * @param string $url
1197 * @return string
1198 */
1199 public static function get_full_path_from_url($url) {
1200 return trailingslashit(WPO_CACHE_FILES_DIR) . trailingslashit(wpo_get_url_path($url));
1201 }
1202
1203 /**
1204 * Admin actions
1205 *
1206 * @return void
1207 */
1208 public function admin_init() {
1209 // Maybe update the advanced cache.
1210 if ((!defined('DOING_AJAX') || !DOING_AJAX) && current_user_can('update_plugins')) {
1211 $this->maybe_update_advanced_cache();
1212 $this->cron_activate();
1213 }
1214 }
1215
1216 /**
1217 * Logs error messages
1218 *
1219 * @param string $message
1220 * @return null|void
1221 */
1222 public function log($message) {
1223 if (isset($this->logger)) {
1224 $this->logger->log($message, 'error');
1225 }
1226 }
1227
1228 /**
1229 * Returns an instance of the current class, creates one if it doesn't exist
1230 *
1231 * @return object
1232 */
1233 public static function instance() {
1234 if (empty(self::$instance)) {
1235 self::$instance = new self();
1236 }
1237 return self::$instance;
1238 }
1239
1240
1241 /**
1242 * Update gmt_offset and timezone_string cache config value, used with hook `update_gmt_offset_timezone_string_config`.
1243 *
1244 * @param {float|string} $old_value GMT offset as a float value or timezone value supported by PHP as a string (https://www.php.net/manual/en/timezones.php)
1245 * @param {float|string} $new_value
1246 * @param {string} $option gmt_offset|timezone_string
1247 *
1248 * @return null
1249 */
1250 public function update_gmt_offset_timezone_string_config($old_value, $new_value, $option) {
1251 if ('' == $new_value) return;
1252
1253 $current_config = $this->config->get();
1254 if ('timezone_string' == $option) {
1255 $timeZone = new DateTimeZone($new_value);
1256 $dateTime = new DateTime("now");
1257 $gmt_offset = $timeZone->getOffset($dateTime);
1258 $current_config['gmt_offset'] = round($gmt_offset/3600, 1);
1259 $current_config['timezone_string'] = $new_value;
1260 } elseif ('' !== $new_value) {
1261 $current_config['gmt_offset'] = $new_value;
1262 $current_config['timezone_string'] = '';
1263 }
1264 $this->config->update($current_config, true);
1265 }
1266
1267 /**
1268 * Update `date_format` cache config value, used with hook `update_option_date_format`.
1269 *
1270 * @param string $old_value Old date format
1271 * @param string $new_value New date format
1272 *
1273 * @return void
1274 */
1275 public function update_option_date_format($old_value, $new_value) {
1276 $current_config = $this->config->get();
1277 $current_config['date_format'] = $new_value;
1278 $this->config->update($current_config, true);
1279 }
1280
1281 /**
1282 * Update `time_format` cache config value, used with hook `update_option_time_format`.
1283 *
1284 * @param string $old_value Old time format
1285 * @param string $new_value New time format
1286 *
1287 * @return void
1288 */
1289 public function update_option_time_format($old_value, $new_value) {
1290 $current_config = $this->config->get();
1291 $current_config['time_format'] = $new_value;
1292 $this->config->update($current_config, true);
1293 }
1294
1295 /**
1296 * Adds an error to the error store
1297 *
1298 * @param string $code - The error code
1299 * @param string $message - The error's message
1300 * @param string $type - The error's type (error, warning)
1301 * @return void
1302 */
1303 public function add_error($code, $message, $type = 'error') {
1304 if (!isset($this->_errors[$type])) {
1305 $this->_errors[$type] = new WP_Error($code, $message);
1306 } else {
1307 $this->_errors[$type]->add($code, $message);
1308 }
1309 }
1310
1311 /**
1312 * Adds a warning to the error store
1313 *
1314 * @param string $code - The error code
1315 * @param string $message - The error's message
1316 * @return void
1317 */
1318 public function add_warning($code, $message) {
1319 $this->add_error($code, $message, 'warning');
1320 }
1321
1322 /**
1323 * Get all recorded errors
1324 *
1325 * @param string $type - The error type
1326 * @param boolean $get_messages_only - Whether to get only the messages, or the full WP_Error object
1327 * @return boolean|array|WP_Error
1328 */
1329 public function get_errors($type = 'error', $get_messages_only = true) {
1330 if (!$this->has_errors($type)) return false;
1331 $errors = $this->_errors[$type];
1332 if ($get_messages_only) {
1333 return $errors->get_error_messages();
1334 }
1335 return $errors;
1336 }
1337
1338 /**
1339 * Check if any errors were recorded
1340 *
1341 * @param string $type - The error type
1342 * @return boolean
1343 */
1344 public function has_errors($type = 'error') {
1345 return isset($this->_errors[$type]) && !empty($this->_errors[$type]) && $this->_errors[$type]->has_errors();
1346 }
1347
1348 /**
1349 * Check if any warnings were recorded
1350 *
1351 * @return boolean
1352 */
1353 public function has_warnings() {
1354 return $this->has_errors('warning');
1355 }
1356
1357 /**
1358 * Check the cache compatibility issues.
1359 */
1360 public function check_compatibility_issues() {
1361 if (!$this->is_enabled()) return;
1362
1363 if ($this->is_pagespeedninja_gzip_active()) add_action('admin_notices', array($this, 'show_pagespeedninja_gzip_notice'));
1364 if ($this->is_farfutureexpiration_gzip_active()) add_action('admin_notices', array($this, 'show_farfutureexpiration_gzip_notice'));
1365 }
1366
1367 /**
1368 * Check if PageSpeed Ninja is active and GZIP compression option is enabled.
1369 *
1370 * @return bool
1371 */
1372 public function is_pagespeedninja_gzip_active() {
1373 if (!class_exists('PagespeedNinja')) return false;
1374
1375 $options = get_option('pagespeedninja_config');
1376 $gzip = !empty($options) ? (bool) $options['psi_EnableGzipCompression'] && (bool) $options['html_gzip'] : false;
1377
1378 return $gzip;
1379 }
1380
1381 /**
1382 * Output PageSpeed Ninja Gzip notice.
1383 */
1384 public function show_pagespeedninja_gzip_notice() {
1385 echo '<div id="wp-optimize-pagespeedninja-gzip-notice" class="error wpo-notice"><p><b>'.__('WP-Optimize:', 'wp-optimize').'</b> '.__('Please disable the feature "Gzip compression" in PageSpeed Ninja to prevent conflicts.', 'wp-optimize').'</p></div>';
1386 }
1387
1388 /**
1389 * Check if Far Future Expiration is active and GZIP compression option is enabled.
1390 *
1391 * @return bool
1392 */
1393 public function is_farfutureexpiration_gzip_active() {
1394 if (!class_exists('farFutureExpiration')) return false;
1395
1396 $options = get_option('far_future_expiration_settings');
1397 $gzip = !empty($options) ? (bool) $options['enable_gzip'] : false;
1398
1399 return $gzip;
1400 }
1401
1402 /**
1403 * Output Far Future Expiration Gzip notice.
1404 */
1405 public function show_farfutureexpiration_gzip_notice() {
1406 echo '<div id="wp-optimize-pagespeedninja-gzip-notice" class="error wpo-notice"><p><b>'.__('WP-Optimize:', 'wp-optimize').'</b> '.__('Please disable the feature "Gzip compression" in Far Future Expiration to prevent conflicts.', 'wp-optimize').'</p></div>';
1407 }
1408
1409 /**
1410 * This is a notice to show users that writing `advanced-cache.php` failed
1411 */
1412 public function show_admin_notice_advanced_cache() {
1413 $message = sprintf(__('The request to write the file %s failed.', 'wp-optimize'), htmlspecialchars($this->get_advanced_cache_filename()));
1414 $message .= ' '.__('Please check file and directory permissions on the file paths up to this point, and your PHP error log.', 'wp-optimize');
1415 WP_Optimize()->include_template('notices/cache-notice.php', false, array('message' => $message));
1416 }
1417
1418 /**
1419 * Scheduler public functions to update schedulers
1420 *
1421 * @param array $schedules An array of schedules being passed.
1422 * @return array An array of schedules being returned.
1423 */
1424 public function cron_schedules($schedules) {
1425 $page_cache_length = $this->config->get_option('page_cache_length');
1426 // When the interval is less than `WP_CRON_LOCK_TIMEOUT` error messages are triggered
1427 // So, instead of `0` (now) we are making it equal to the constant value.
1428 // https://github.com/WordPress/WordPress/commit/84d19848666a81584e0007a6ab7f7ad3c990d71a
1429 if (defined('WP_CRON_LOCK_TIMEOUT') && $page_cache_length < WP_CRON_LOCK_TIMEOUT) {
1430 $page_cache_length = WP_CRON_LOCK_TIMEOUT;
1431 }
1432
1433 $schedules['wpo_purge_old_cache'] = array('interval' => false === $page_cache_length ? 86400 : $page_cache_length, 'display' => __('Every time after the cache has expired', 'wp-optimize'));
1434
1435 $interval = WP_Optimize_Page_Cache_Preloader::instance()->get_continue_preload_cron_interval();
1436 $schedules['wpo_page_cache_preload_continue_interval'] = array(
1437 'interval' => $interval,
1438 'display' => sprintf(__('%d minutes', 'wp-optimize'), round($interval / 60, 1))
1439 );
1440
1441 $schedules['wpo_use_cache_lifespan'] = array(
1442 'interval' => $page_cache_length,
1443 'display' => sprintf(__('Same as cache lifespan: %s', 'wp-optimize'), WPO_Cache_Config::instance()->get_option('page_cache_length_value').' '.WPO_Cache_Config::instance()->get_option('page_cache_length_unit'))
1444 );
1445
1446 return $schedules;
1447 }
1448
1449 /**
1450 * Update cache config file to reflect the webp images option
1451 */
1452 public function update_webp_images_option() {
1453 if ($this->is_enabled()) {
1454 $cache_settings = WPO_Cache_Config::instance()->get();
1455 $cache_settings['use_webp_images'] = WP_Optimize()->get_options()->get_option('webp_conversion');
1456 WPO_Cache_Config::instance()->update($cache_settings);
1457 }
1458 }
1459
1460 /**
1461 * May be regenerate cache config file, in case of migrations
1462 */
1463 public function maybe_regenerate_cache_config_file() {
1464 $config_file = WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename();
1465 if (!file_exists($config_file)) {
1466 $config = $this->config->get();
1467 $this->config->write($config);
1468 }
1469 }
1470
1471 /**
1472 * Checks if the given cache directory is empty
1473 *
1474 * @param string $path The path to the cache directory.
1475 * @return bool Returns true if the cache directory is empty, false otherwise.
1476 */
1477 public static function is_cache_empty($path) {
1478 if (!is_dir($path)) return true;
1479
1480 if ($handle = opendir($path)) {
1481 while (false !== ($entry = readdir($handle))) {
1482 if ("." == $entry || ".." == $entry) {
1483 continue;
1484 }
1485
1486 $full_path = $path . '/' . $entry;
1487 if (is_file($full_path)) {
1488 closedir($handle);
1489 return false;
1490 }
1491 }
1492 closedir($handle);
1493 return true;
1494 }
1495 return true;
1496 }
1497 }
1498
1499 endif;
1500