PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.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
← All changes | cache/class-wpo-page-cache.php +463 -167 3.7.1 → 4.7.0 View file →
@@ -21,19 +21,10 @@
21 21 * Directory that stores config and related files
22 22 */
23 23 if (!defined('WPO_CACHE_CONFIG_DIR')) define('WPO_CACHE_CONFIG_DIR', WPO_CACHE_DIR.'/config');
24 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 25 require_once dirname(__FILE__) . '/file-based-page-cache-functions.php';
31 26
32 -if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
33 - require_once dirname(__FILE__) . '/php-5.3-functions.php';
34 -}
35 -
36 27 wpo_cache_load_extensions();
37 28
38 29 if (!class_exists('WPO_Page_Cache')) :
39 30
@@ -53,11 +44,18 @@
53 44 */
54 45 public $logger;
55 46
56 47 /**
48 + * File Logger for this class
49 + *
50 + * @var mixed
51 + */
52 + private $file_logger;
53 +
54 + /**
57 55 * Instance of this class
58 56 *
59 - * @var mixed
57 + * @var WPO_Page_Cache | null
60 58 */
61 59 public static $instance;
62 60
63 61 /**
@@ -88,22 +86,44 @@
88 86 * @var string
89 87 */
90 88 private $_minimum_advanced_cache_file_version = '3.0.17';
91 89
90 + /**
91 + * Flag to decide whether to purge or not
92 + *
93 + * @var bool
94 + */
92 95 public $should_purge;
93 96
94 97 /**
98 + * Flag to decide whether to purge or not
99 + *
100 + * @var bool
101 + */
102 + private $files_to_ignore = array('index.php','.htaccess');
103 +
104 + /**
95 105 * Set everything up here
96 106 */
97 107 public function __construct() {
98 108 $this->config = WPO_Cache_Config::instance();
109 + if (empty($GLOBALS['wpo_cache_config'])) {
110 + $config_file_path = $this->config->get_config_file_path();
111 + if (file_exists($config_file_path)) {
112 + include_once($config_file_path);
113 + }
114 + }
115 +
99 116 WPO_Cache_Rules::instance();
100 117 WP_Optimize_Page_Cache_Preloader::instance();
118 +
101 119 $this->logger = new Updraft_PHP_Logger();
120 + $this->file_logger = new Updraft_File_Logger(WP_Optimize_Utils::get_log_file_path('cache'));
102 121
103 122 add_action('activated_plugin', array($this, 'activate_deactivate_plugin'));
104 123 add_action('deactivate_plugin', array($this, 'activate_deactivate_plugin'));
105 124 add_action('wpo_purge_old_cache', array($this, 'purge_old'));
125 + add_action('wpo_prune_cache_logs', array($this, 'prune_cache_logs'));
106 126
107 127 /**
108 128 * Regenerate config file on cache flush.
109 129 */
@@ -108,8 +128,9 @@
108 128 * Regenerate config file on cache flush.
109 129 */
110 130 add_action('wpo_cache_flush', array($this, 'update_cache_config'));
111 131 add_action('wpo_cache_flush', array($this, 'delete_cache_size_information'));
132 + add_action('wpo_cache_flush', array($this, 'update_cache_cleared_time'));
112 133 add_action('update_option_permalink_structure', array($this, 'update_option_permalink_structure'), 10, 3);
113 134
114 135 // Add purge cache link to admin bar.
115 136 add_filter('wpo_cache_admin_bar_menu_items', array($this, 'admin_bar_purge_cache'), 20, 1);
@@ -125,29 +146,140 @@
125 146 add_action('update_option_time_format', array($this, 'update_option_time_format'), 10, 2);
126 147
127 148 $this->check_compatibility_issues();
128 149
129 - add_filter('cron_schedules', array($this, 'cron_schedules'));
150 + $this->ensure_cron_schedules_filter_registered();
151 +
130 152 add_action('wpo_save_images_settings', array($this, 'update_webp_images_option'));
131 153
132 154 add_action('wpo_preload_url', array($this, 'maybe_preload_url'));
155 +
156 + // Setup filters for exceptions.
157 + add_filter('wpo_restricted_cache_page_type', 'wpo_restricted_cache_page_type');
158 + add_filter('wpo_url_in_conditional_tags_exceptions', 'wpo_url_in_conditional_tags_exceptions');
133 159 }
134 160
135 161 /**
162 + * Ensures that the cron-related filter is registered.
163 + *
164 + * If `init` has already run, the filter is registered immediately.
165 + * Otherwise, it schedules the registration to occur during `init`.
166 + *
167 + * @return void
168 + */
169 + private function ensure_cron_schedules_filter_registered() {
170 + if (did_action('init')) {
171 + $this->register_cron_schedules_filter();
172 + } else {
173 + add_action('init', array($this, 'register_cron_schedules_filter'));
174 + }
175 + }
176 +
177 + /**
178 + * Registers the custom cron schedule filter for WP-Cron.
179 + *
180 + * This method hooks the class's `cron_schedules()` method into WordPress's
181 + * `cron_schedules` filter, allowing the plugin to define custom cron intervals.
182 + *
183 + * It is recommended to call this method on or after the `init` action to ensure
184 + * that translations and other dependencies are properly loaded before the filter runs.
185 + *
186 + * @return void
187 + */
188 + public function register_cron_schedules_filter() {
189 + add_filter('cron_schedules', array($this, 'cron_schedules'));
190 + }
191 +
192 + /**
193 + * Determines whether the current page should be cached.
194 + *
195 + * This method checks cache rules to identify if caching is possible.
196 + * If caching is not allowed, it adds appropriate HTTP headers and debug messages.
197 + *
198 + * @return bool|array True if the page should be cached, false otherwise or not empty array with the reasons why the page isn't cached
199 + */
200 + public function should_cache_page() {
201 +
202 + if (!$this->is_enabled()) return false;
203 +
204 + return wpo_can_serve_from_cache();
205 + }
206 +
207 + /**
208 + * Maybe add information about why the page is not cached to the headers and footer output
209 + *
210 + * @param array $no_cache_because
211 + * @return void
212 + */
213 + public function maybe_add_no_cache_because_info($no_cache_because) {
214 + // Add http header
215 + if (!wp_doing_cron() && !empty($no_cache_because)) {
216 + $no_cache_because_message = join(", ", $no_cache_because);
217 + wpo_cache_add_nocache_http_header_with_send_headers_action($no_cache_because_message);
218 +
219 + $not_cached_details = "";
220 +
221 + // Output the reason only when the user has turned on debugging
222 + if ((defined('WP_DEBUG') && WP_DEBUG) || !empty(TeamUpdraft\WP_Optimize\Includes\Fragments\fetch_superglobal('get', 'wpo_cache_debug'))) {
223 + $not_cached_details = "because: ".$no_cache_because_message;
224 + }
225 + wpo_cache_add_footer_output(sprintf("Page not served from cache %s", $not_cached_details));
226 + }
227 + }
228 +
229 + /**
230 + * Handle cache settings update
231 + *
232 + * @param array $new_settings
233 + * @return void
234 + */
235 + public function cache_settings_updated($new_settings) {
236 +
237 + $this->unschedule_purge_old_cache();
238 +
239 + if ($new_settings['enable_page_caching']) {
240 + $this->schedule_purge_old_cache();
241 + }
242 + }
243 +
244 + /**
136 245 * Activate cron job when the cache is enabled for deleting expired cache files
137 246 */
138 247 public function cron_activate() {
139 - $page_cache_length = $this->config->get_option('page_cache_length');
140 248 if ($this->is_enabled()) {
141 - if (!wp_next_scheduled('wpo_purge_old_cache')) {
142 - wp_schedule_event(time() + (false === $page_cache_length ? '86400' : $page_cache_length), 'wpo_purge_old_cache', 'wpo_purge_old_cache');
143 - }
249 + $this->schedule_purge_old_cache();
144 250 } else {
145 - wp_clear_scheduled_hook('wpo_purge_old_cache');
251 + $this->unschedule_purge_old_cache();
146 252 }
147 253 }
148 254
149 255 /**
256 + * Schedules events to purge old cache
257 + *
258 + * @return void
259 + */
260 + private function schedule_purge_old_cache() {
261 + $page_cache_length = $this->config->get_option('page_cache_length');
262 +
263 + if (!wp_next_scheduled('wpo_purge_old_cache')) {
264 + wp_schedule_event(time() + (false === $page_cache_length ? 86400 : $page_cache_length), 'wpo_purge_old_cache', 'wpo_purge_old_cache');
265 + }
266 + if (!wp_next_scheduled('wpo_prune_cache_logs')) {
267 + wp_schedule_event(time(), 'weekly', 'wpo_prune_cache_logs');
268 + }
269 + }
270 +
271 + /**
272 + * Unschedules events to purge old cache
273 + *
274 + * @return void
275 + */
276 + private function unschedule_purge_old_cache() {
277 + wp_clear_scheduled_hook('wpo_purge_old_cache');
278 + wp_clear_scheduled_hook('wpo_prune_cache_logs');
279 + }
280 +
281 + /**
150 282 * Do required actions on activate/deactivate any plugin.
151 283 */
152 284 public function activate_deactivate_plugin() {
153 285
@@ -154,9 +286,11 @@
154 286 wp_clear_scheduled_hook('wpo_purge_old_cache');
155 287
156 288 $this->update_cache_config();
157 289
158 - $this->purge();
290 + $is_cache_purged = $this->purge();
291 +
292 + if ($is_cache_purged) $this->file_log("Full Cache Purge triggered by: plugin activation/deactivation");
159 293 }
160 294
161 295 /**
162 296 * Check if current user can purge cache.
@@ -164,15 +298,10 @@
164 298 * @return bool
165 299 */
166 300 public function can_purge_cache() {
167 301 if (!$this->is_enabled()) return false;
168 - $required_capability = is_multisite() ? 'manage_network_options' : 'manage_options';
169 302
170 - if (WP_Optimize::is_premium()) {
171 - return current_user_can($required_capability) || WP_Optimize_Premium()->can_purge_the_cache();
172 - } else {
173 - return current_user_can($required_capability);
174 - }
303 + return WP_Optimize_Utils::current_user_can_purge_cache();
175 304 }
176 305
177 306 /**
178 307 * Add Purge from cache in admin bar.
@@ -183,12 +312,13 @@
183 312 public function admin_bar_purge_cache($menu_items) {
184 313 global $pagenow;
185 314 if (!$this->can_purge_cache()) return $menu_items;
186 315
187 - $act_url = remove_query_arg(array('wpo_single_page_cache_purged', 'wpo_all_pages_cache_purged'));
316 + $act_url = WP_Optimize_Utils::get_url_without_cache_purge_params();
188 317
189 318 $cache_size = $this->get_cache_size();
190 319 $cache_size_info = '<h4>'.__('Page cache', 'wp-optimize').'</h4>';
320 + // translators: %d is the number of files in the cache
191 321 $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>';
192 322
193 323 $menu_items[] = array(
194 324 'id' => 'wpo_cache_stats',
@@ -208,9 +338,9 @@
208 338 ),
209 339 'parent' => 'wpo_purge_cache',
210 340 );
211 341
212 - if (!is_admin() || 'post.php' == $pagenow) {
342 + if (!is_admin() || 'post.php' === $pagenow) {
213 343 $menu_items[] = array(
214 344 'id' => 'wpo_purge_this_page_cache',
215 345 'title' => __('Purge cache for this page', 'wp-optimize'),
216 346 'href' => add_query_arg('_wpo_purge', wp_create_nonce('wpo_purge_single_page_cache'), $act_url),
@@ -232,22 +362,38 @@
232 362
233 363 if (!$this->can_purge_cache()) return;
234 364
235 365 if (isset($_GET['wpo_single_page_cache_purged']) || isset($_GET['wpo_all_pages_cache_purged'])) {
366 + // phpcs:disable
367 + // We are not using $_GET values, just checks if the variable is set and then use hard coded string
236 368 if (isset($_GET['wpo_single_page_cache_purged'])) {
237 - $notice_function = $_GET['wpo_single_page_cache_purged'] ? 'notice_purge_single_page_cache_success' : 'notice_purge_single_page_cache_error';
369 + $message = $_GET['wpo_single_page_cache_purged'] ? __('Cache purged for this page', 'wp-optimize') : __('Error purging cache for this page', 'wp-optimize');
370 + $type = $_GET['wpo_single_page_cache_purged'] ? 'success' : 'error';
238 371 } else {
239 - $notice_function = $_GET['wpo_all_pages_cache_purged'] ? 'notice_purge_all_pages_cache_success' : 'notice_purge_all_pages_cache_error';
372 + $message = $_GET['wpo_all_pages_cache_purged'] ? __('Cache purged for all pages', 'wp-optimize') : __('Error purging cache for all pages', 'wp-optimize');
373 + $type = $_GET['wpo_all_pages_cache_purged'] ? 'success' : 'error';
240 374 }
375 + // phpcs:enable
241 376
242 - add_action('admin_notices', array($this, $notice_function));
377 + if (is_admin()) {
378 + $admin_notices_hook = is_network_admin() ? 'network_admin_notices' : 'admin_notices';
243 379
380 + add_action($admin_notices_hook, function() use ($message, $type) {
381 + $this->show_notice($message, $type);
382 + });
383 +
384 + add_action('admin_head', array('WP_Optimize_Utils', 'script_to_remove_cache_purge_params_from_url'));
385 + } else {
386 + printf('<script>window.onload = function() {alert("%s");}</script>', esc_js($message));
387 + WP_Optimize_Utils::script_to_remove_cache_purge_params_from_url();
388 + }
389 +
244 390 return;
245 391 }
246 392
247 393 if (!isset($_GET['_wpo_purge'])) return;
248 394
249 - if (wp_verify_nonce($_GET['_wpo_purge'], 'wpo_purge_single_page_cache')) {
395 + if (wp_verify_nonce(sanitize_key($_GET['_wpo_purge']), 'wpo_purge_single_page_cache')) {
250 396 $success = false;
251 397
252 398 if (is_admin()) {
253 399 $post = isset($_GET['post']) ? (int) $_GET['post'] : 0;
@@ -252,23 +398,33 @@
252 398 if (is_admin()) {
253 399 $post = isset($_GET['post']) ? (int) $_GET['post'] : 0;
254 400 if ($post > 0) {
255 401 $success = self::delete_single_post_cache($post);
402 + if ($success) $this->file_log("Cache for URL: {{URL}} has been purged, triggered by: " . __METHOD__, $post);
256 403 }
257 404 } else {
258 - $success = self::delete_cache_by_url(wpo_current_url());
405 + $url = wpo_current_url();
406 + $success = self::delete_cache_by_url($url);
407 + if ($success) $this->file_log("Cache for URL: " . self::remove_query_params($url) . " has been purged, triggered by: " . __METHOD__);
259 408 }
260 409
261 - // remove nonce from url and reload page.
262 - wp_redirect(add_query_arg('wpo_single_page_cache_purged', $success, remove_query_arg('_wpo_purge')));
410 + // Remove all existing purge-related parameters and add the new one
411 + $redirect_url = WP_Optimize_Utils::get_url_without_cache_purge_params();
412 + $redirect_url = add_query_arg('wpo_single_page_cache_purged', $success, $redirect_url);
413 +
414 + wp_redirect($redirect_url);
263 415 exit;
264 416
265 - } elseif (wp_verify_nonce($_GET['_wpo_purge'], 'wpo_purge_all_pages_cache')) {
417 + } elseif (wp_verify_nonce(sanitize_key($_GET['_wpo_purge']), 'wpo_purge_all_pages_cache')) {
266 418 $success = self::purge();
267 419 $this->maybe_set_preload_cron_job();
420 + if ($success) $this->file_log("Full Cache Purge triggered by: ". __METHOD__);
268 421
269 - // remove nonce from url and reload page.
270 - wp_redirect(add_query_arg('wpo_all_pages_cache_purged', $success, remove_query_arg('_wpo_purge')));
422 + // Remove all existing purge-related parameters and add the new one
423 + $redirect_url = WP_Optimize_Utils::get_url_without_cache_purge_params();
424 + $redirect_url = add_query_arg('wpo_all_pages_cache_purged', $success, $redirect_url);
425 +
426 + wp_redirect($redirect_url);
271 427 exit;
272 428 }
273 429 }
274 430
@@ -288,38 +444,10 @@
288 444 wp_schedule_single_event(time() + 30, 'wpo_page_cache_run_preload');
289 445 }
290 446 }
291 447 }
292 -
448 +
293 449 /**
294 - * Show notification when page cache purged successfully.
295 - */
296 - public function notice_purge_single_page_cache_success() {
297 - $this->show_notice(__('The page cache was successfully purged.', 'wp-optimize'), 'success');
298 - }
299 -
300 - /**
301 - * Show notification when page cache wasn't purged.
302 - */
303 - public function notice_purge_single_page_cache_error() {
304 - $this->show_notice(__('The page cache was not purged.', 'wp-optimize'), 'error');
305 - }
306 -
307 - /**
308 - * Show notification when all pages cache purged successfully.
309 - */
310 - public function notice_purge_all_pages_cache_success() {
311 - $this->show_notice(__('The page cache was successfully purged.', 'wp-optimize'), 'success');
312 - }
313 -
314 - /**
315 - * Show notification when all pages cache wasn't purged.
316 - */
317 - public function notice_purge_all_pages_cache_error() {
318 - $this->show_notice(__('The page cache was not purged.', 'wp-optimize'), 'error');
319 - }
320 -
321 - /**
322 450 * Show notification in WordPress admin.
323 451 *
324 452 * @param string $message HTML (no further escaping is performed)
325 453 * @param string $type error, warning, success, or info
@@ -332,10 +460,10 @@
332 460 window.addEventListener('load', function() {
333 461 (function(wp) {
334 462 if (window.wp && wp.hasOwnProperty('data') && 'function' == typeof wp.data.dispatch) {
335 463 wp.data.dispatch('core/notices').createNotice(
336 - '<?php echo $type; ?>',
337 - '<?php echo $message; ?>',
464 + '<?php echo esc_html($type); ?>',
465 + '<?php echo wp_kses_post($message); ?>',
338 466 {
339 467 isDismissible: true,
340 468 }
341 469 );
@@ -343,10 +471,10 @@
343 471 })(window.wp);
344 472 });
345 473 </script>
346 474 <?php else : ?>
347 - <div class="notice wpo-notice notice-<?php echo $type; ?> is-dismissible">
348 - <p><?php echo $message; ?></p>
475 + <div class="notice wpo-notice notice-<?php echo esc_attr($type); ?> is-dismissible">
476 + <p><?php echo wp_kses_post($message); ?></p>
349 477 </div>
350 478 <?php
351 479 endif;
352 480 }
@@ -358,8 +486,9 @@
358 486 *
359 487 * @return WP_Error|bool - true on success, error otherwise
360 488 */
361 489 public function enable($force_enable = false) {
490 + global $is_apache;
362 491 static $already_ran_enable = false;
363 492
364 493 if ($already_ran_enable) return $already_ran_enable;
365 494
@@ -380,11 +509,18 @@
380 509 }
381 510
382 511 $this->maybe_regenerate_cache_config_file();
383 512
513 + // Enable `.htaccess` rules to serve cached files
514 + if ($is_apache && apply_filters('wpo_serve_cache_via_htaccess', false)) {
515 + wpo_disable_cache_directories_viewing();
516 + wpo_allow_access_to_index_cache_files();
517 + $this->update_serve_cache_rules_htaccess_section(true);
518 + }
519 +
384 520 if (!$force_enable) {
385 - $already_ran_enable = true;
386 - return true;
521 + $already_ran_enable = $this->update_page_cache_enabled_state(true);
522 + return $already_ran_enable;
387 523 }
388 524
389 525 if (!$this->write_advanced_cache() && version_compare($this->get_advanced_cache_version(), $this->_minimum_advanced_cache_file_version, '<')) {
390 526 $message = sprintf("The request to write the file %s failed. ", htmlspecialchars($this->get_advanced_cache_filename()));
@@ -390,8 +526,9 @@
390 526 $message = sprintf("The request to write the file %s failed. ", htmlspecialchars($this->get_advanced_cache_filename()));
391 527 $message .= ' '.__('Please check file and directory permissions on the file paths up to this point, and your PHP error log.', 'wp-optimize');
392 528
393 529 if (!defined('WP_CLI') || !WP_CLI) {
530 + // translators: %s is the path to advanced-cache.php folder
394 531 $message .= "\n\n1. ".sprintf(__('Please navigate, via FTP, to the folder - %s', 'wp-optimize'), htmlspecialchars(dirname($this->get_advanced_cache_filename())));
395 532 $message .= "\n2. ".__('Edit or create a file with the name advanced-cache.php', 'wp-optimize');
396 533 $message .= "\n3. ".__('Copy and paste the following lines into the file:', 'wp-optimize');
397 534 }
@@ -410,19 +547,21 @@
410 547 $already_ran_enable = new WP_Error("verify_cache", "Could not verify if the cache was enabled: \n".implode("\n- ", $errors));
411 548 return $already_ran_enable;
412 549 }
413 550
414 - $already_ran_enable = true;
415 -
416 - return true;
551 + $already_ran_enable = $this->update_page_cache_enabled_state(true);
552 + return $already_ran_enable;
417 553 }
418 554
419 555 /**
420 556 * Disables page cache
421 557 *
422 - * @return bool - true on success, false otherwise
558 + * @param bool $keep_option_enabled - Whether to keep the option `enable_page_caching` enabled or not. This is useful when we want to disable the cache temporarily, e.g. when deactivating the plugin, but we want to keep the option enabled so that it can be re-enabled when the plugin is reactivated.
559 + * @return bool|WP_Error - true on success or WP_Error on failure.
423 560 */
424 - public function disable() {
561 + public function disable($keep_option_enabled = false) {
562 + global $is_apache;
563 +
425 564 $ret = true;
426 565
427 566 $advanced_cache_file = $this->get_advanced_cache_filename();
428 567
@@ -428,15 +567,8 @@
428 567
429 568 // 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.
430 569 $disabled_wp_config = $this->write_wp_config(false);
431 570 if (!$disabled_wp_config) {
432 - $plugin_basename = basename(WPO_PLUGIN_MAIN_PATH);
433 - $action = "deactivate_".$plugin_basename."/wp-optimize.php";
434 - if (current_action() === $action) {
435 - $cache_config = WPO_Cache_Config::instance()->config;
436 - $cache_config['enable_page_caching'] = false;
437 - WPO_Cache_Config::instance()->update($cache_config, true);
438 - }
439 571 $this->log("Could not turn off the WP_CACHE constant in wp-config.php");
440 572 $this->add_warning('error_disabling', __('Could not turn off the WP_CACHE constant in wp-config.php', 'wp-optimize'));
441 573 }
442 574
@@ -443,9 +575,9 @@
443 575 $disabled_advanced_cache = true;
444 576 // First try to remove (so that it doesn't look to any other plugin like the file is already 'claimed')
445 577 // We only touch advanched-cache.php and wp-config.php if it appears that we were in control of advanced-cache.php
446 578 if (!file_exists($advanced_cache_file) || false !== strpos(file_get_contents($advanced_cache_file), 'WP-Optimize advanced-cache.php')) {
447 - if (file_exists($advanced_cache_file) && (!unlink($advanced_cache_file) && false === file_put_contents($advanced_cache_file, "<?php\n// WP-Optimize: page cache disabled"))) {
579 + if (file_exists($advanced_cache_file) && (!WP_Optimize_Utils::wp_delete_file($advanced_cache_file) && false === file_put_contents($advanced_cache_file, "<?php\n// WP-Optimize: page cache disabled"))) {
448 580 $disabled_advanced_cache = false;
449 581 $this->log("The request to the filesystem to remove or empty advanced-cache.php failed");
450 582 $this->add_warning('error_disabling', __('The request to the filesystem to remove or empty advanced-cache.php failed', 'wp-optimize'));
451 583 }
@@ -456,17 +588,55 @@
456 588 $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'));
457 589 }
458 590
459 591 // Delete cache to avoid stale cache on next activation
460 - $this->purge();
592 + $is_cache_purged = $this->purge();
593 + if ($is_cache_purged) $this->file_log("Full Cache Purge triggered by: ". __METHOD__);
461 594
595 + // Delete `.htaccess` rules to serve cached files
596 + if ($is_apache && apply_filters('wpo_serve_cache_via_htaccess', false)) {
597 + $this->update_serve_cache_rules_htaccess_section(false);
598 + }
599 +
462 600 // Unschedule cron job to purge old cache
463 601 wp_clear_scheduled_hook('wpo_purge_old_cache');
464 602
603 + if (!is_wp_error($ret)) {
604 + wp_clear_scheduled_hook('wpo_prune_cache_logs');
605 +
606 + if (!$keep_option_enabled) {
607 + $ret = $this->update_page_cache_enabled_state(false);
608 + }
609 + }
610 +
611 + if (!is_wp_error($ret)) {
612 + do_action('wpo_page_cache_disabled');
613 + }
614 +
465 615 return $ret;
466 616 }
467 617
618 + /**
619 + * Update page cache enabled state.
620 + * Only updates `enable_page_caching` if the option exists and the value has changed.
621 + *
622 + * @param bool $enabled Whether page caching should be marked as enabled.
623 + *
624 + * @return true|WP_Error true on success, WP_Error on failure.
625 + */
626 + private function update_page_cache_enabled_state($enabled) {
627 + $config = WPO_Cache_Config::instance();
628 + $cache_config = $config->get();
468 629
630 + if (isset($cache_config['enable_page_caching']) && $cache_config['enable_page_caching'] === $enabled) {
631 + return true;
632 + }
633 +
634 + $cache_config['enable_page_caching'] = $enabled;
635 +
636 + return $config->update($cache_config, true);
637 + }
638 +
469 639 /**
470 640 * Purges the cache
471 641 *
472 642 * @return bool - true on success, false otherwise
@@ -508,12 +678,12 @@
508 678 if (0 === $page_cache_length) return $log;
509 679 $expires = time() - $page_cache_length;
510 680 $cache_folder = WPO_CACHE_FILES_DIR . '/' . str_ireplace(array('http://', 'https://'), '', get_site_url());
511 681 // get all directories that are a direct child of current directory
512 - if (is_dir($cache_folder) && is_writable($cache_folder)) {
682 + if (is_dir($cache_folder) && wp_is_writable($cache_folder)) {
513 683 if ($handle = opendir($cache_folder)) {
514 684 while (false !== ($d = readdir($handle))) {
515 - if (0 == strcmp($d, '.') || 0 == strcmp($d, '..')) {
685 + if (0 === strcmp($d, '.') || 0 === strcmp($d, '..')) {
516 686 continue;
517 687 }
518 688
519 689 if ($this->is_front_page_cache($d)) {
@@ -518,9 +688,9 @@
518 688
519 689 if ($this->is_front_page_cache($d)) {
520 690 $modified_time = (int) filemtime("$cache_folder/$d");
521 691 if ($modified_time <= $expires) {
522 - unlink("$cache_folder/$d");
692 + wp_delete_file("$cache_folder/$d");
523 693 }
524 694 continue;
525 695 }
526 696
@@ -532,9 +702,9 @@
532 702 $log[] = "checking if cache has expired - $d";
533 703 if ($modified_time <= $expires) {
534 704 $log[] = "deleting cache in $dir";
535 705 wpo_delete_files($dir, true);
536 - if (file_exists($dir)) rmdir($dir);
706 + if (file_exists($dir)) rmdir($dir); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- N/A
537 707 }
538 708 }
539 709 closedir($handle);
540 710 }
@@ -568,36 +738,40 @@
568 738 return true;
569 739 }
570 740
571 741 /**
572 - * Check if cache is enabled and working
742 + * Checks whether page caching is turned on in settings.
573 743 *
574 - * @return bool - true on success, false otherwise
744 + * @return bool true if the `enable_page_caching` option is set & environment is ready to cache, false otherwise.
575 745 */
576 746 public function is_enabled() {
747 + return $this->config->get_option('enable_page_caching') && $this->is_environment_functional();
748 + }
577 749
578 - if (!$this->config->get_option('enable_page_caching')) return false;
750 + /**
751 + * Checks whether the cache delivery chain is actually intact and able to serve cached pages.
752 + *
753 + * Verifies the WP_CACHE and WPO_ADVANCED_CACHE constants are defined and true, and that the cache config file exists on disk.
754 + *
755 + * @return bool True if the delivery chain is functional, false otherwise.
756 + */
757 + private function is_environment_functional() {
758 + $is_cli_or_ajax = (defined('WP_CLI') && WP_CLI) || (defined('DOING_AJAX') && DOING_AJAX);
579 759
580 - if (!defined('WP_CACHE') || !WP_CACHE) {
581 - if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
582 - $this->log("WP_CACHE constant is not present in wp-config.php");
583 - return false;
584 - }
760 + if (!$is_cli_or_ajax && (!defined('WP_CACHE') || !WP_CACHE)) {
761 + $this->file_log("WP_CACHE constant is not present in wp-config.php");
762 + return false;
585 763 }
586 764
587 - if (!defined('WPO_ADVANCED_CACHE') || !WPO_ADVANCED_CACHE) {
588 - if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
589 - $this->log("WPO_ADVANCED_CACHE constant is not present in advanced-cache.php");
590 - return false;
591 - }
765 + if (!$is_cli_or_ajax && (!defined('WPO_ADVANCED_CACHE') || !WPO_ADVANCED_CACHE)) {
766 + $this->file_log("WPO_ADVANCED_CACHE constant is not present in advanced-cache.php");
767 + return false;
592 768 }
593 769
594 770 $config_file = WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename();
595 - if (!file_exists($config_file)) {
596 - if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
597 - $this->log("Cache config file $config_file is not present");
598 - return false;
599 - }
771 + if (!$is_cli_or_ajax && !file_exists($config_file)) {
772 + $this->file_log("Cache config file $config_file is not present");
773 + return false;
600 774 }
601 775
602 776 return true;
603 777 }
@@ -604,23 +778,26 @@
604 778
605 779 /**
606 780 * Create the folder structure needed for cache to work
607 781 *
608 - * @return bool - true on success, false otherwise
782 + * @return bool | WP_Error - true on success, false otherwise
609 783 */
610 784 public function create_folders() {
611 785 $permissions_str = __('Please check your file permissions.', 'wp-optimize');
612 786
613 787 if (!is_dir(WPO_CACHE_DIR) && !wp_mkdir_p(WPO_CACHE_DIR)) {
788 + // translators: %s is a directory name
614 789 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);
615 790 }
616 791
617 792 if (!is_dir(WPO_CACHE_CONFIG_DIR) && !wp_mkdir_p(WPO_CACHE_CONFIG_DIR)) {
793 + // translators: %s is a directory name
618 794 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);
619 795 }
620 796
621 797 if (!is_dir(WPO_CACHE_FILES_DIR)) {
622 798 if (!wp_mkdir_p(WPO_CACHE_FILES_DIR)) {
799 + // translators: %s is a directory name
623 800 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);
624 801 } else {
625 802 wpo_disable_cache_directories_viewing();
626 803 }
@@ -651,9 +828,9 @@
651 828 $cache_path = '/wpo-cache';
652 829 $cache_files_path = '/cache/wpo-cache';
653 830 $cache_extensions_path = WPO_CACHE_EXT_DIR;
654 831 $wpo_version = WPO_VERSION;
655 - $wpo_home_url = trailingslashit(home_url());
832 + $wpo_home_url = trailingslashit(network_home_url());
656 833 $abspath = ABSPATH;
657 834
658 835 // CS does not like heredoc
659 836 // phpcs:disable
@@ -734,15 +911,15 @@
734 911 }
735 912
736 913 // check if we can't write the advanced cache file
737 914 // case 1: the directory is read-only and the file doesn't exist
738 - if (!is_file($advanced_cache_filename) && !is_writable(dirname($advanced_cache_filename))) {
915 + if (!is_file($advanced_cache_filename) && !wp_is_writable(dirname($advanced_cache_filename))) {
739 916 $this->advanced_cache_file_writing_error = true;
740 917 return false;
741 918 }
742 919
743 920 // case 2: the file already exists but it's read-only
744 - if (is_file($advanced_cache_filename) && !is_writable($advanced_cache_filename)) {
921 + if (is_file($advanced_cache_filename) && !wp_is_writable($advanced_cache_filename)) {
745 922 if (version_compare($this->get_advanced_cache_version(), $this->_minimum_advanced_cache_file_version, '<') || $update_required) {
746 923 $this->advanced_cache_file_writing_error = true;
747 924 return false;
748 925 } else {
@@ -781,9 +958,10 @@
781 958 if ($advanced_cache_current_version && version_compare($advanced_cache_current_version, $this->_minimum_advanced_cache_file_version, '>=') && !$is_the_site_url_or_folder_changed) return;
782 959
783 960 // Purge the cache when the site url or folder changed.
784 961 if ($is_the_site_url_or_folder_changed) {
785 - $this->purge();
962 + $is_cache_purged = $this->purge();
963 + if ($is_cache_purged) $this->file_log("Full Cache Purge triggered by: ". __METHOD__);
786 964 }
787 965
788 966 if (!$this->write_advanced_cache()) {
789 967 add_action('admin_notices', array($this, 'notice_advanced_cache_autoupdate_error'));
@@ -804,11 +982,11 @@
804 982
805 983 if (false === $content) return false;
806 984
807 985 if (preg_match('/WP\-Optimize advanced\-cache\.php \(written by version\: (.+)\) (\(homeurl: (.+)\)) (\(abspath: (.+)\)) \(do not change/Ui', $content, $match)) {
808 - $wpo_home_url = trailingslashit(home_url());
986 + $wpo_home_url = trailingslashit(network_home_url());
809 987 $abspath = ABSPATH;
810 - return ($wpo_home_url != $match[3] || $abspath != $match[5]);
988 + return ($wpo_home_url !== $match[3] || $abspath !== $match[5]);
811 989 } elseif (preg_match('/WP\-Optimize advanced\-cache\.php \(written by version\: (.+)\)/Ui', $content, $match)) {
812 990 return true;
813 991 }
814 992
@@ -861,8 +1039,13 @@
861 1039 if (!$config_path) {
862 1040 return false;
863 1041 }
864 1042
1043 + // Bail early if wp-config.php is not writable, to avoid a PHP warning.
1044 + if (!wp_is_writable($config_path)) {
1045 + return false;
1046 + }
1047 +
865 1048 $config_file_string = file_get_contents($config_path);
866 1049
867 1050 // Config file is empty. Maybe couldn't read it?
868 1051 if (empty($config_file_string)) {
@@ -915,9 +1098,9 @@
915 1098 }
916 1099 $errors = 0;
917 1100
918 1101 // First check wp-config.php.
919 - if (!$this->_get_wp_config() && !is_writable($this->_get_wp_config())) {
1102 + if (!$this->_get_wp_config() && !wp_is_writable($this->_get_wp_config())) {
920 1103 $this->log("Unable to write to or find wp-config.php; please check file/folder permissions");
921 1104 $this->add_warning('verify_cache', __("Unable to write to or find wp-config.php; please check file/folder permissions.", 'wp-optimize'));
922 1105 }
923 1106
@@ -923,9 +1106,9 @@
923 1106
924 1107 $advanced_cache_file = untrailingslashit(WP_CONTENT_DIR).'/advanced-cache.php';
925 1108
926 1109 // Now check wp-content. We need to be able to create files of the same user as this file.
927 - 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))) {
1110 + if ((!file_exists($advanced_cache_file) || false === strpos(file_get_contents($advanced_cache_file), 'WP-Optimize advanced-cache.php')) && !wp_is_writable($advanced_cache_file) && !wp_is_writable(untrailingslashit(WP_CONTENT_DIR))) {
928 1111 $this->log("Unable to write the file advanced-cache.php inside the wp-content folder; please check file/folder permissions");
929 1112 $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'));
930 1113 $errors++;
931 1114 }
@@ -930,16 +1113,17 @@
930 1113 $errors++;
931 1114 }
932 1115
933 1116 if (file_exists(WPO_CACHE_FILES_DIR)) {
934 - if (!is_writable(WPO_CACHE_FILES_DIR)) {
1117 + if (!wp_is_writable(WPO_CACHE_FILES_DIR)) {
935 1118 $this->log("Unable to write inside the cache files folder; please check file/folder permissions");
1119 + // translators: %s is cache folder path
936 1120 $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));
937 1121 }
938 1122 }
939 1123
940 1124 if (file_exists(WPO_CACHE_CONFIG_DIR)) {
941 - if (!is_writable(WPO_CACHE_CONFIG_DIR)) {
1125 + if (!wp_is_writable(WPO_CACHE_CONFIG_DIR)) {
942 1126 $this->log("Unable to write inside the cache configuration folder; please check file/folder permissions");
943 1127 // If the config exists, only send a warning. Otherwise send an error.
944 1128 $type = 'warning';
945 1129 if (!file_exists(WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename())) {
@@ -945,8 +1129,9 @@
945 1129 if (!file_exists(WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename())) {
946 1130 $type = 'error';
947 1131 $errors++;
948 1132 }
1133 + // translators: %s is cache config folder path
949 1134 $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);
950 1135 }
951 1136 }
952 1137
@@ -961,9 +1146,9 @@
961 1146 * @param string $option Option name `permalink_structure`
962 1147 */
963 1148 public function update_option_permalink_structure($old_value, $value, $option) {
964 1149 $current_config = $this->config->get();
965 - if ($old_value != $value) {
1150 + if ($old_value !== $value) {
966 1151 $current_config[$option] = $value;
967 1152 $this->config->update($current_config, true);
968 1153 }
969 1154 }
@@ -970,9 +1155,9 @@
970 1155
971 1156 /**
972 1157 * Update cache config. Used to support 3d party plugins.
973 1158 *
974 - * @return {boolean|WP_Error}
1159 + * @return bool | WP_Error
975 1160 */
976 1161 public function update_cache_config() {
977 1162 // get current cache settings.
978 1163 $current_config = $this->config->get();
@@ -983,18 +1168,29 @@
983 1168 /**
984 1169 * Delete information about cache size.
985 1170 */
986 1171 public function delete_cache_size_information() {
1172 + //not removing delete_transient so it clear this key from option table
987 1173 delete_transient('wpo_get_cache_size');
1174 + delete_site_transient('wpo_get_cache_size');
988 1175 }
989 1176
990 1177 /**
1178 + * Update cache cleared time
1179 + */
1180 + public function update_cache_cleared_time(): void {
1181 + $config = $this->config->get();
1182 + $config['last_cleared'] = time();
1183 + $this->config->update($config);
1184 + }
1185 +
1186 + /**
991 1187 * Get current cache size.
992 1188 *
993 1189 * @return array
994 1190 */
995 1191 public function get_cache_size() {
996 - $cache_size = get_transient('wpo_get_cache_size');
1192 + $cache_size = get_site_transient('wpo_get_cache_size');
997 1193
998 1194 if (!empty($cache_size)) return $cache_size;
999 1195
1000 1196 $infos = $this->get_dir_infos(WPO_CACHE_FILES_DIR);
@@ -1002,9 +1198,11 @@
1002 1198 'size' => $infos['size'],
1003 1199 'file_count' => $infos['file_count']
1004 1200 );
1005 1201
1006 - set_transient('wpo_get_cache_size', $cache_size);
1202 + $expiration = apply_filters('wpo_cache_size_expiration', DAY_IN_SECONDS);
1203 + $expiration = is_numeric($expiration) ? (int) $expiration : DAY_IN_SECONDS;
1204 + set_site_transient('wpo_get_cache_size', $cache_size, $expiration);
1007 1205
1008 1206 return $cache_size;
1009 1207 }
1010 1208
@@ -1014,39 +1212,12 @@
1014 1212 * @param string $dir
1015 1213 * @return array
1016 1214 */
1017 1215 private function get_dir_infos($dir) {
1018 - $dir_size = 0;
1019 - $file_count = 0;
1216 + $filtered = apply_filters('wpo_cache_size_files_to_ignore', $this->files_to_ignore);
1217 + $this->files_to_ignore = is_array($filtered) ? $filtered : array();
1020 1218
1021 - $handle = is_dir($dir) ? opendir($dir) : false;
1022 -
1023 - if (false === $handle) {
1024 - return array('size' => 0, 'file_count' => 0);
1025 - }
1026 -
1027 - $file = readdir($handle);
1028 -
1029 - while (false !== $file) {
1030 -
1031 - if ('.' != $file && '..' != $file) {
1032 - $current_file = $dir.'/'.$file;
1033 -
1034 - if (is_dir($current_file)) {
1035 - $sub_dir_infos = $this->get_dir_infos($current_file);
1036 - $dir_size += $sub_dir_infos['size'];
1037 - $file_count += $sub_dir_infos['file_count'];
1038 - } elseif (is_file($current_file)) {
1039 - $dir_size += filesize($current_file);
1040 - $file_count++;
1041 - }
1042 - }
1043 -
1044 - $file = readdir($handle);
1045 -
1046 - }
1047 -
1048 - return array('size' => $dir_size, 'file_count' => $file_count);
1219 + return WP_Optimize_Utils::get_folder_stats($dir, $this->files_to_ignore);
1049 1220 }
1050 1221
1051 1222 /**
1052 1223 * Returns the path to wp-config
@@ -1090,9 +1261,9 @@
1090 1261 *
1091 1262 * @return bool
1092 1263 */
1093 1264 public static function delete_cache_by_url($url, $recursive = false) {
1094 - if (!defined('WPO_CACHE_FILES_DIR') || '' == $url) return;
1265 + if (!defined('WPO_CACHE_FILES_DIR') || '' === $url) return false;
1095 1266
1096 1267 $path = self::get_full_path_from_url($url);
1097 1268
1098 1269 do_action('wpo_delete_cache_by_url', $url, $recursive);
@@ -1122,8 +1293,10 @@
1122 1293 $preloader = WP_Optimize_Page_Cache_Preloader::instance();
1123 1294 $preloader->add_url_to_preload_list($url);
1124 1295 $url_task_creator = array($preloader, 'create_tasks_for_auto_preload_urls');
1125 1296
1297 + $preloader->maybe_dont_close_browser_connection();
1298 +
1126 1299 if (!has_action('shutdown', $url_task_creator)) {
1127 1300 add_action('shutdown', $url_task_creator);
1128 1301 }
1129 1302 }
@@ -1152,9 +1325,9 @@
1152 1325 * @return bool
1153 1326 */
1154 1327 public static function really_delete_single_post_cache($post_id) {
1155 1328
1156 - if (!defined('WPO_CACHE_FILES_DIR')) return;
1329 + if (!defined('WPO_CACHE_FILES_DIR')) return false;
1157 1330
1158 1331 $post_url = get_permalink($post_id);
1159 1332
1160 1333 $path = self::get_full_path_from_url($post_url);
@@ -1206,8 +1379,10 @@
1206 1379 }
1207 1380
1208 1381 /**
1209 1382 * Delete post feed from cache.
1383 + *
1384 + * @param int $post_id Post ID
1210 1385 */
1211 1386 public static function delete_post_feed_cache($post_id) {
1212 1387 self::really_delete_post_feed_cache($post_id);
1213 1388
@@ -1215,8 +1390,10 @@
1215 1390 }
1216 1391
1217 1392 /**
1218 1393 * Really delete post feed from cache.
1394 + *
1395 + * @param int $post_id Post ID
1219 1396 */
1220 1397 public static function really_delete_post_feed_cache($post_id) {
1221 1398 if (!defined('WPO_CACHE_FILES_DIR')) return;
1222 1399
@@ -1268,9 +1445,9 @@
1268 1445 * @return void
1269 1446 */
1270 1447 public function admin_init() {
1271 1448 // Maybe update the advanced cache.
1272 - if ((!defined('DOING_AJAX') || !DOING_AJAX) && current_user_can('update_plugins')) {
1449 + if ((!defined('DOING_AJAX') || !DOING_AJAX) && WP_Optimize()->current_user_can('update_plugins')) {
1273 1450 $this->maybe_update_advanced_cache();
1274 1451 $this->cron_activate();
1275 1452 }
1276 1453 }
@@ -1289,9 +1466,9 @@
1289 1466
1290 1467 /**
1291 1468 * Returns an instance of the current class, creates one if it doesn't exist
1292 1469 *
1293 - * @return object
1470 + * @return self
1294 1471 */
1295 1472 public static function instance() {
1296 1473 if (empty(self::$instance)) {
1297 1474 self::$instance = new self();
@@ -1302,19 +1479,19 @@
1302 1479
1303 1480 /**
1304 1481 * Update gmt_offset and timezone_string cache config value, used with hook `update_gmt_offset_timezone_string_config`.
1305 1482 *
1306 - * @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)
1307 - * @param {float|string} $new_value
1308 - * @param {string} $option gmt_offset|timezone_string
1483 + * @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)
1484 + * @param float | string $new_value
1485 + * @param string $option gmt_offset|timezone_string
1309 1486 *
1310 1487 * @return null
1311 1488 */
1312 1489 public function update_gmt_offset_timezone_string_config($old_value, $new_value, $option) {
1313 - if ('' == $new_value) return;
1490 + if ('' === $new_value) return;
1314 1491
1315 1492 $current_config = $this->config->get();
1316 - if ('timezone_string' == $option) {
1493 + if ('timezone_string' === $option) {
1317 1494 $timeZone = new DateTimeZone($new_value);
1318 1495 $dateTime = new DateTime("now");
1319 1496 $gmt_offset = $timeZone->getOffset($dateTime);
1320 1497 $current_config['gmt_offset'] = round($gmt_offset/3600, 1);
@@ -1434,9 +1611,9 @@
1434 1611 public function is_pagespeedninja_gzip_active() {
1435 1612 if (!class_exists('PagespeedNinja')) return false;
1436 1613
1437 1614 $options = get_option('pagespeedninja_config');
1438 - $gzip = !empty($options) ? (bool) $options['psi_EnableGzipCompression'] && (bool) $options['html_gzip'] : false;
1615 + $gzip = isset($options['psi_EnableGzipCompression']) && isset($options['html_gzip']) && (bool) $options['psi_EnableGzipCompression'] && (bool) $options['html_gzip'];
1439 1616
1440 1617 return $gzip;
1441 1618 }
1442 1619
@@ -1443,9 +1620,9 @@
1443 1620 /**
1444 1621 * Output PageSpeed Ninja Gzip notice.
1445 1622 */
1446 1623 public function show_pagespeedninja_gzip_notice() {
1447 - 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>';
1624 + echo '<div id="wp-optimize-pagespeedninja-gzip-notice" class="error wpo-notice"><p><b>'.esc_html__('WP-Optimize:', 'wp-optimize').'</b> '.esc_html__('Please disable the feature "Gzip compression" in PageSpeed Ninja to prevent conflicts.', 'wp-optimize').'</p></div>';
1448 1625 }
1449 1626
1450 1627 /**
1451 1628 * Check if Far Future Expiration is active and GZIP compression option is enabled.
@@ -1455,9 +1632,9 @@
1455 1632 public function is_farfutureexpiration_gzip_active() {
1456 1633 if (!class_exists('farFutureExpiration')) return false;
1457 1634
1458 1635 $options = get_option('far_future_expiration_settings');
1459 - $gzip = !empty($options) ? (bool) $options['enable_gzip'] : false;
1636 + $gzip = !empty($options) && $options['enable_gzip'];
1460 1637
1461 1638 return $gzip;
1462 1639 }
1463 1640
@@ -1464,9 +1641,9 @@
1464 1641 /**
1465 1642 * Output Far Future Expiration Gzip notice.
1466 1643 */
1467 1644 public function show_farfutureexpiration_gzip_notice() {
1468 - 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>';
1645 + echo '<div id="wp-optimize-pagespeedninja-gzip-notice" class="error wpo-notice"><p><b>'.esc_html__('WP-Optimize:', 'wp-optimize').'</b> '.esc_html__('Please disable the feature "Gzip compression" in Far Future Expiration to prevent conflicts.', 'wp-optimize').'</p></div>';
1469 1646 }
1470 1647
1471 1648 /**
1472 1649 * This is a notice to show users that writing `advanced-cache.php` failed
@@ -1471,8 +1648,9 @@
1471 1648 /**
1472 1649 * This is a notice to show users that writing `advanced-cache.php` failed
1473 1650 */
1474 1651 public function show_admin_notice_advanced_cache() {
1652 + // translators: %s is advanced-cache.php file path
1475 1653 $message = sprintf(__('The request to write the file %s failed.', 'wp-optimize'), htmlspecialchars($this->get_advanced_cache_filename()));
1476 1654 $message .= ' '.__('Please check file and directory permissions on the file paths up to this point, and your PHP error log.', 'wp-optimize');
1477 1655 WP_Optimize()->include_template('notices/cache-notice.php', false, array('message' => $message));
1478 1656 }
@@ -1496,13 +1674,15 @@
1496 1674
1497 1675 $interval = WP_Optimize_Page_Cache_Preloader::instance()->get_continue_preload_cron_interval();
1498 1676 $schedules['wpo_page_cache_preload_continue_interval'] = array(
1499 1677 'interval' => $interval,
1678 + // translators: %d is the number of minutes
1500 1679 'display' => sprintf(__('%d minutes', 'wp-optimize'), round($interval / 60, 1))
1501 1680 );
1502 1681
1503 1682 $schedules['wpo_use_cache_lifespan'] = array(
1504 1683 'interval' => $page_cache_length,
1684 + // translators: %s is the cache lifespan
1505 1685 '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'))
1506 1686 );
1507 1687
1508 1688 return $schedules;
@@ -1519,9 +1699,9 @@
1519 1699 }
1520 1700 }
1521 1701
1522 1702 /**
1523 - * May be regenerate cache config file, in case of migrations
1703 + * Maybe regenerate cache config file, in case of migrations
1524 1704 */
1525 1705 public function maybe_regenerate_cache_config_file() {
1526 1706 $config_file = WPO_CACHE_CONFIG_DIR . '/'.$this->config->get_cache_config_filename();
1527 1707 if (!file_exists($config_file)) {
@@ -1540,9 +1720,9 @@
1540 1720 if (!is_dir($path)) return true;
1541 1721
1542 1722 if ($handle = opendir($path)) {
1543 1723 while (false !== ($entry = readdir($handle))) {
1544 - if ("." == $entry || ".." == $entry) {
1724 + if ("." === $entry || ".." === $entry) {
1545 1725 continue;
1546 1726 }
1547 1727
1548 1728 $full_path = $path . '/' . $entry;
@@ -1557,17 +1737,133 @@
1557 1737 return true;
1558 1738 }
1559 1739
1560 1740 /**
1741 + * Remove query params from request url
1742 + *
1743 + * @param string $url The request url.
1744 + * @return string Returns url without query params.
1745 + */
1746 + private static function remove_query_params($url) {
1747 + $parsed_url = wp_parse_url($url);
1748 + if (!$parsed_url) return '';
1749 + return $parsed_url['scheme'] . '://' . $parsed_url['host'] . $parsed_url['path'];
1750 + }
1751 +
1752 + /**
1561 1753 * Check configuration for auto preloading after cache deletion
1562 1754 *
1563 1755 * @return bool
1564 1756 */
1565 1757 public function should_auto_preload_purged_contents() {
1758 +
1759 + if (!$this->is_enabled()) return false; // as Page Cache is not enabled we should not auto preload purged content.
1760 +
1566 1761 $wpo_cache = WP_Optimize()->get_page_cache();
1567 1762 $wpo_cache_options = $wpo_cache->config->get();
1568 -
1763 +
1569 1764 return $wpo_cache_options['auto_preload_purged_contents'];
1765 + }
1766 +
1767 + /**
1768 + * Logging of interesting messages related to Cache Purging
1769 + *
1770 + * @param string $message
1771 + * @param int $post_id
1772 + */
1773 + public function file_log($message, $post_id = 0) {
1774 + if (!empty($post_id)) {
1775 + if (false !== strpos($message, '{{URL}}')) {
1776 + $message = str_replace('{{URL}}', get_permalink($post_id), $message);
1777 + }
1778 + if (false !== strpos($message, '{{title}}')) {
1779 + $message = str_replace('{{title}}', '"' . get_the_title($post_id) . '"', $message);
1780 + }
1781 + }
1782 + $this->file_logger->info($message);
1783 + }
1784 +
1785 + /**
1786 + * Prunes the log file
1787 + */
1788 + public function prune_cache_logs() {
1789 + $this->file_log("Pruning the Cache log file");
1790 + $this->file_logger->prune_logs();
1791 + }
1792 +
1793 + /**
1794 + * Update .htaccess file to serve cached files directly.
1795 + *
1796 + * @param bool $enable
1797 + */
1798 + public function update_serve_cache_rules_htaccess_section($enable) {
1799 + $htaccess = new WP_Optimize_Htaccess();
1800 +
1801 + $section_title = 'WP-Optimize rules to serve cached files';
1802 +
1803 + if ($enable) {
1804 + if (!$htaccess->is_commented_section_exists($section_title)) {
1805 + $htaccess->update_commented_section($this->prepare_apache_cache_handle_section(), $section_title, true);
1806 + $htaccess->write_file();
1807 + }
1808 + } elseif ($htaccess->is_commented_section_exists($section_title)) {
1809 + $htaccess->remove_commented_section($section_title);
1810 + $htaccess->write_file();
1811 + }
1812 +
1813 + }
1814 +
1815 + /**
1816 + * Get the .htaccess section with rules to serve cached files
1817 + *
1818 + * @return array
1819 + */
1820 + private function prepare_apache_cache_handle_section() {
1821 +
1822 + $site_root = $this->get_site_root_path();
1823 +
1824 + return array(
1825 + array(
1826 + '<IfModule mod_rewrite.c>',
1827 + 'RewriteEngine On',
1828 + 'RewriteCond %{REQUEST_METHOD} GET|HEAD',
1829 + 'RewriteCond %{QUERY_STRING} =""',
1830 + 'RewriteCond %{HTTP:Cookie} =""',
1831 + 'RewriteCond %{REQUEST_URI} !^/(wp-(?:admin|login|register|comments-post|cron|json))/ [NC]',
1832 + 'RewriteCond %{HTTP_USER_AGENT} !(android|blackberry|ipad|iphone|ipod|windows.phone|iEMobile|opera.mini|opera.mobile|webos|symbian|windows.mobile|kindle|googlebot.mobile) [NC]',
1833 + 'RewriteCond %{DOCUMENT_ROOT}'.$site_root.'wp-content/cache/wpo-cache/%{HTTP_HOST}%{REQUEST_URI}index.html -f',
1834 + 'RewriteRule ^(.*)$ '.$site_root.'wp-content/cache/wpo-cache/%{HTTP_HOST}%{REQUEST_URI}index.html [L]',
1835 + array(
1836 + '<IfModule mod_setenvif.c>',
1837 + 'SetEnvIf Request_URI "/wp-content/cache/wpo-cache/" FROM_CACHE=1',
1838 + '</IfModule>',
1839 + ),
1840 + array(
1841 + '<IfModule mod_headers.c>',
1842 + 'Header always set X-WP-Optimize-Cache-Header "Loaded from WP-Optimize cache" env=FROM_CACHE',
1843 + '</IfModule>',
1844 + ),
1845 + '</IfModule>',
1846 + ),
1847 + );
1848 + }
1849 +
1850 + /**
1851 + * Get relative site root path, if site is placed in the subdirectory then it returns /subdirectory otherwise /
1852 + *
1853 + * @param string $default
1854 + *
1855 + * @return string
1856 + */
1857 + private function get_site_root_path($default = '/') {
1858 + $site_root = wp_parse_url(site_url());
1859 + if (isset($site_root['path'])) {
1860 + $site_root = trailingslashit($site_root['path']);
1861 + } else {
1862 + $site_root = $default;
1863 + }
1864 +
1865 + return $site_root;
1570 1866 }
1571 1867 }
1572 1868
1573 1869 endif;