PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.6.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.6.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-cache-rules.php

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

485 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('No direct access allowed');
4
5 /**
6 * Page caching rules and exceptions
7 */
8
9 require_once dirname(__FILE__) . '/file-based-page-cache-functions.php';
10
11 if (!class_exists('WPO_Cache_Rules')) :
12
13 class WPO_Cache_Rules {
14
15 /**
16 * Cache config object
17 *
18 * @var mixed
19 */
20 public $config;
21
22 /**
23 * Instance of this class
24 *
25 * @var WPO_Cache_Rules | null
26 */
27 public static $instance;
28
29 public function __construct() {
30 $this->config = WPO_Cache_Config::instance()->get();
31 $this->setup_hooks();
32 }
33
34 /**
35 * Setup hooks/filters
36 */
37 public function setup_hooks() {
38 add_action('save_post', array($this, 'purge_post_on_update'), 10, 1);
39 add_action('save_post', array($this, 'purge_archive_pages_on_post_update'), 10, 1);
40 add_action('wp_trash_post', array($this, 'purge_post_on_update'), 10, 1);
41 add_action('comment_post', array($this, 'purge_post_on_comment'), 10, 3);
42 add_action('wp_set_comment_status', array($this, 'purge_post_on_comment_status_change'), 10, 1);
43 add_action('edit_terms', array($this, 'purge_related_elements_on_term_updated'), 10, 2);
44 add_action('set_object_terms', array($this, 'purge_related_elements_on_post_terms_change'), 10, 6);
45 add_action('wpo_cache_config_updated', array($this, 'cache_config_updated'), 10, 1);
46 add_action('wp_insert_comment', array($this, 'comment_inserted'), 10, 2);
47 add_action('import_start', array($this, 'remove_wp_insert_comment'));
48
49 add_action('update_option_page_on_front', array($this, 'purge_cache_on_homepage_change'));
50 add_action('update_option_page_for_posts', array($this, 'purge_cache_on_blog_page_change'), 10, 2);
51 add_action('update_option_show_avatars', array($this, 'purge_cache_on_avatars_change'), 10, 2);
52
53 add_action('woocommerce_variation_set_stock', array($this, 'purge_product_page'), 10, 1);
54 add_action('woocommerce_product_set_stock', array($this, 'purge_product_page'), 10, 1);
55
56 add_filter('wpo_page_cache_preloader_dont_close_browser_connection', array($this, 'preloader_dont_close_browser_connection'), 10, 1);
57
58 /**
59 * List of hooks for which when executed, the cache will be purged
60 *
61 * @param array $actions The actions
62 */
63 $actions = array(
64 'after_switch_theme',
65 'wp_update_nav_menu',
66 'customize_save_after',
67 array('wp_ajax_save-widget', 0),
68 array('wp_ajax_update-widget', 0),
69 'autoptimize_action_cachepurged',
70 'upgrader_overwrote_package',
71 'wpo_active_plugin_or_theme_updated',
72 'fusion_cache_reset_after',
73 'update_option_permalink_structure',
74 'update_option_posts_per_page',
75 'wpml_st_add_string_translation',
76 );
77 $purge_on_action = apply_filters('wpo_purge_cache_hooks', $actions);
78 foreach ($purge_on_action as $action) {
79 if (is_array($action)) {
80 add_action($action[0], array($this, 'purge_cache'), $action[1]);
81 } else {
82 add_action($action, array($this, 'purge_cache'));
83 }
84 }
85 }
86
87 /**
88 * Purge post cache when there is a new approved comment
89 *
90 * @param int $comment_id Comment ID.
91 * @param int|string $approved Comment approved status. can be 0, 1 or 'spam'.
92 * @param array $commentdata Comment data array. Always sent be WP core, but a plugin was found that does not send it - https://wordpress.org/support/topic/critical-problems-with-version-3-0-10/
93 */
94 public function purge_post_on_comment($comment_id, $approved, $commentdata = array()) {
95 if (1 !== $approved) {
96 return;
97 }
98
99 if (!empty($this->config['enable_page_caching']) && !empty($commentdata['comment_post_ID'])) {
100 $post_id = $commentdata['comment_post_ID'];
101
102 WPO_Page_Cache::delete_single_post_cache($post_id);
103 WPO_Page_Cache::delete_comments_feed();
104 WPO_Page_Cache::instance()->file_log("Cache for URL: {{URL}} has been purged, triggered by action: " . current_filter(), $post_id);
105 }
106 }
107
108 /**
109 * Every time a comment's status changes, purge its parent posts cache
110 *
111 * @param int $comment_id Comment ID.
112 */
113 public function purge_post_on_comment_status_change($comment_id) {
114 if (!empty($this->config['enable_page_caching'])) {
115 $comment = get_comment($comment_id);
116 if (is_object($comment) && !empty($comment->comment_post_ID)) {
117 WPO_Page_Cache::delete_single_post_cache($comment->comment_post_ID);
118 WPO_Page_Cache::delete_comments_feed();
119 WPO_Page_Cache::instance()->file_log("Cache for URL: {{URL}} has been purged, triggered by action: " . current_filter(), $comment->comment_post_ID);
120 }
121 }
122 }
123
124 /**
125 * Action when a comment is inserted
126 *
127 * @param integer $comment_id - The comment ID
128 * @param boolean|WP_Comment $comment - The comment object (from WP 4.4)
129 * @return void
130 */
131 public function comment_inserted($comment_id, $comment = false) {
132 if ($comment && is_a($comment, 'WP_Comment')) {
133 /**
134 * Filters whether to add a cookie when a comment is posted, in order to exclude the page from caching.
135 * Regular comments have the property comment_type set to '' or 'comment'. So by default, only add the cookie in those cases.
136 *
137 * @param boolean $add_cookie
138 * @param WP_Comment $comment
139 * @return boolean
140 */
141 $add_cookie = apply_filters('wpo_add_commented_post_cookie', '' === $comment->comment_type || 'comment' === $comment->comment_type, $comment);
142 if (!$add_cookie) return;
143
144 $url = get_permalink($comment->comment_post_ID);
145 $url_info = wp_parse_url($url);
146 setcookie('wpo_commented_post', 1, time() + WEEK_IN_SECONDS, $url_info['path'] ?? '/');
147 }
148 }
149
150 /**
151 * Comment posted cookie is not needed for imports. So remove the action
152 */
153 public function remove_wp_insert_comment() {
154 remove_action('wp_insert_comment', array($this, 'comment_inserted'), 10);
155 }
156
157 /**
158 * Automatically purge all file based page cache on post changes
159 * We want the whole cache purged here as different parts
160 * of the site could potentially change on post updates
161 *
162 * @param int $post_id - WP post id
163 */
164 public function purge_post_on_update($post_id) {
165 $post_type = get_post_type($post_id);
166 $post_type_object = get_post_type_object($post_type);
167
168 if ((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || 'revision' === $post_type || null === $post_type_object || !$post_type_object->public) {
169 return;
170 }
171 $post_status = get_post_status($post_id);
172 if ('publish' !== $post_status) {
173 return;
174 }
175
176 /**
177 * Purge the whole cache if set to true, only the edited post otherwise. Default is false.
178 *
179 * @param boolean $purge_all_cache The default filter value
180 * @param integer $post_id The saved post ID
181 */
182 if (apply_filters('wpo_purge_all_cache_on_update', false, $post_id)) {
183 $this->purge_cache();
184 } else {
185 if (apply_filters('wpo_delete_cached_homepage_on_post_update', true, $post_id)) WPO_Page_Cache::delete_homepage_cache();
186 WPO_Page_Cache::delete_feed_cache();
187 WPO_Page_Cache::delete_single_post_cache($post_id);
188 WPO_Page_Cache::delete_post_feed_cache($post_id);
189 WPO_Page_Cache::instance()->file_log("Cache for URL: {{URL}} has been purged, triggered by action: " . current_filter(), $post_id);
190 }
191 }
192
193 /**
194 * Purge archive pages on post update.
195 *
196 * @param integer $post_id
197 */
198 public function purge_archive_pages_on_post_update($post_id) {
199 $post_type = get_post_type($post_id);
200
201 if (false === $post_type) return;
202
203 if ((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || 'revision' === $post_type) {
204 return;
205 }
206
207 $post_obj = get_post_type_object($post_type);
208
209 if (null === $post_obj) return;
210
211 $post_status = get_post_status($post_id);
212 if ('publish' !== $post_status) {
213 return;
214 }
215
216 if ('post' === $post_type) {
217 // delete blog page cache
218 $blog_post_id = get_option('page_for_posts');
219 if ($blog_post_id) {
220 WPO_Page_Cache::delete_cache_by_url(get_permalink($blog_post_id), true);
221 WPO_Page_Cache::instance()->file_log("Cache for blog post has been purged due to updates to post with Title: {{title}} and URL: {{URL}}, triggered by action: " . current_filter(), $post_id);
222 }
223
224 // delete next and previous posts cache.
225 $globals_post = $GLOBALS['post'] ?? false;
226 $GLOBALS['post'] = get_post($post_id);
227 $previous_post = function_exists('get_previous_post') ? get_previous_post() : false;
228 $next_post = function_exists('get_next_post') ? get_next_post() : false;
229 if ($globals_post) $GLOBALS['post'] = $globals_post;
230
231 if ($previous_post) {
232 WPO_Page_Cache::delete_cache_by_url(get_permalink($previous_post), true);
233 WPO_Page_Cache::instance()->file_log("Cache for previous post (with URL: {{URL}}) adjacent to post with id: " . $post_id . " has been purged, triggered by action: " . current_filter(), $previous_post->ID);
234 }
235 if ($next_post) {
236 WPO_Page_Cache::delete_cache_by_url(get_permalink($next_post), true);
237 WPO_Page_Cache::instance()->file_log("Cache for next post (with URL: {{URL}}) adjacent to post with id: " . $post_id . " has been purged, triggered by action: " . current_filter(), $next_post->ID);
238 }
239
240 // delete all archive pages for post.
241 $post_date = get_post_time('Y-m-j', false, $post_id);
242 list($year, $month, $day) = $post_date;
243
244 $archive_links = array(
245 get_year_link($year),
246 get_month_link($year, $month),
247 get_day_link($year, $month, $day),
248 );
249
250 foreach ($archive_links as $link) {
251 WPO_Page_Cache::delete_cache_by_url($link, true);
252 }
253 WPO_Page_Cache::instance()->file_log("Cache for all archives page associated with post_type: " . $post_type . " and Title: {{title}} have been purged, triggered by action: " . current_filter(), $post_id);
254 }
255 if ($post_obj->has_archive) {
256 // delete archive page for custom post type.
257 WPO_Page_Cache::delete_cache_by_url(get_post_type_archive_link($post_type), true);
258 WPO_Page_Cache::instance()->file_log("Cache for archive page associated with post_type: " . $post_type . " and Title: {{title}} has been purged, triggered by action: " . current_filter(), $post_id);
259 }
260 if (post_type_supports($post_type, 'author')) {
261 $author_id = get_post_field('post_author', $post_id);
262 $post_author = get_user_by('id', $author_id);
263 if ($author_id && $post_author) {
264 $author_url = get_author_posts_url($author_id);
265 if (!empty($author_url)) {
266 WPO_Page_Cache::delete_cache_by_url($author_url, true);
267 WPO_Page_Cache::instance()->file_log("Cache for author page URL: " . $author_url . " associated with post_type: " . $post_type . " and Title: {{title}} has been purged, triggered by action: " . current_filter(), $post_id);
268 }
269 }
270 }
271 }
272
273 /**
274 * We use it with edit_terms action filter to purge cached elements related
275 * to updated term when term updated.
276 *
277 * @param int $term_id Term taxonomy ID.
278 * @param string $taxonomy Taxonomy slug.
279 */
280 public function purge_related_elements_on_term_updated($term_id, $taxonomy) {
281 if ('nav_menu' === $taxonomy) return;
282
283 // purge cached page for term.
284 $term = get_term($term_id, $taxonomy, ARRAY_A);
285 if (is_array($term)) {
286 $term_permalink = get_term_link($term['term_id']);
287 if (!is_wp_error($term_permalink)) {
288 WPO_Page_Cache::delete_cache_by_url($term_permalink, true);
289 WPO_Page_Cache::instance()->file_log("Cache for term page with URL: " . $term_permalink . " have been purged, triggered by action: " . current_filter());
290 }
291 }
292
293 // get posts which belongs to updated term.
294 $posts = get_posts(array(
295 'numberposts' => -1,
296 'post_type' => 'any',
297 'fields' => 'ids',
298 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- because we can't use `get_objects_in_term` for `OR` relationships
299 'relation' => 'OR',
300 array(
301 'taxonomy' => $taxonomy,
302 'field' => 'term_id',
303 'terms' => $term_id,
304 )
305 ),
306 ));
307
308 if (!empty($posts)) {
309 foreach ($posts as $post_id) {
310 WPO_Page_Cache::delete_single_post_cache($post_id);
311 }
312 $term_permalink = get_term_link($term_id);
313 WPO_Page_Cache::instance()->file_log("Cache for associated posts for term with URL: "
314 . $term_permalink. " have been purged, triggered by action: " . current_filter());
315 }
316 }
317
318 /**
319 * Triggered by set_object_terms action. Used to clear all the terms archives a post belongs to or belonged to before being saved.
320 *
321 * @param int $object_id Object ID.
322 * @param array $terms An array of object terms.
323 * @param array $tt_ids An array of term taxonomy IDs.
324 * @param string $taxonomy Taxonomy slug.
325 * @param bool $append Whether to append new terms to the old terms.
326 * @param array $old_tt_ids Old array of term taxonomy IDs.
327 */
328 public function purge_related_elements_on_post_terms_change($object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids) {
329
330 $post_type = get_post_type($object_id);
331
332 if ((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || 'revision' === $post_type || 'product_type' === $taxonomy || 'action-group' === $taxonomy) {
333 return;
334 }
335
336 /**
337 * Adds a way to exit the purge of terms permalink using the provided parameters.
338 *
339 * @param bool $purge The value filtered, whether to purge the related elements or not
340 * @param int $object_id Object ID.
341 * @param array $terms An array of object terms.
342 * @param array $tt_ids An array of term taxonomy IDs.
343 * @param string $taxonomy Taxonomy slug.
344 * @param bool $append Whether to append new terms to the old terms.
345 * @param array $old_tt_ids Old array of term taxonomy IDs.
346 * @default true
347 * @return boolean
348 */
349 if (!apply_filters('wpo_cache_purge_related_elements_on_post_terms_change', true, $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids)) return;
350
351 // get all affected terms.
352 $affected_terms_ids = array_unique(array_merge($tt_ids, $old_tt_ids));
353
354 if (!empty($affected_terms_ids)) {
355 // walk through all changed terms and purge cached pages for them.
356 $is_cache_purged = false;
357 foreach ($affected_terms_ids as $tt_id) {
358 $term = get_term($tt_id, $taxonomy, ARRAY_A);
359 if (!is_array($term)) continue;
360
361 $term_permalink = get_term_link($term['term_id']);
362 if (!is_wp_error($term_permalink)) {
363 $url = wp_parse_url($term_permalink);
364 // Check if the permalink contains a valid path, to avoid deleting the whole cache.
365 if (!isset($url['path']) || '/' === $url['path']) return;
366 WPO_Page_Cache::delete_cache_by_url($term_permalink, true);
367 $is_cache_purged = true;
368 }
369 }
370 if ($is_cache_purged) WPO_Page_Cache::instance()->file_log("Cache for multiple term taxonomy have been purged due to post terms update, triggered by action: " . current_filter());
371 }
372 }
373
374 /**
375 * Purge product page upon stock update
376 */
377 public function purge_product_page($product_with_stock) {
378 if (!empty($product_with_stock->get_id())) {
379 WPO_Page_Cache::delete_single_post_cache($product_with_stock->get_id());
380 WPO_Page_Cache::instance()->file_log("Cache for URL: {{URL}} has been purged, triggered by action: " . current_filter(), $product_with_stock->get_id());
381 }
382 }
383
384 /**
385 * Purges relevant caches when the "Homepage" option is changed.
386 *
387 * This method is also triggered when the "Homepage displays" option is changed
388 * from a static page to latest posts. In this scenario, the "Homepage" option value
389 * becomes zero. Since the cache for the front-page is already purged here, there's no need
390 * to purge the cache using the`update_option_show_on_front` hook.
391 *
392 * @param string $old_value The old value of the "Homepage" option.
393 */
394 public function purge_cache_on_homepage_change($old_value) {
395 if ($old_value) {
396 WPO_Page_Cache::delete_cache_by_url(get_permalink($old_value));
397 }
398 WPO_Page_Cache::delete_homepage_cache();
399 WPO_Page_Cache::instance()->file_log("Cache for homepage has been purged due to change in the Homepage displays options, triggered by action: " . current_filter());
400 }
401
402 /**
403 * Purges relevant caches when the "Posts page" option is changed.
404 *
405 * @param string $old_value The old value of the "Posts page" option.
406 * @param string $value The new value of the "Posts page" option.
407 */
408 public function purge_cache_on_blog_page_change($old_value, $value) {
409 if ($old_value) {
410 WPO_Page_Cache::delete_cache_by_url(get_permalink($old_value));
411 }
412 if ($value) {
413 WPO_Page_Cache::delete_cache_by_url(get_permalink($value));
414 }
415 WPO_Page_Cache::instance()->file_log("Cache for homepage has been purged due to changes in the Posts page options, triggered by action: " . current_filter());
416 }
417
418 /**
419 * Purges cache if the avatars option is changed
420 *
421 * @param boolean $old_value
422 * @param boolean $value
423 */
424 public function purge_cache_on_avatars_change($old_value, $value) {
425 if ($old_value !== $value) {
426 $this->purge_cache();
427 }
428 }
429
430 /**
431 * Clears the cache.
432 */
433 public function purge_cache() {
434 if (!empty($this->config['enable_page_caching'])) {
435 wpo_cache_flush();
436 WPO_Page_Cache::instance()->file_log("Full Cache Purge triggered by action: " . current_filter());
437 }
438 }
439
440 /**
441 * Filter to set the flag to not close the browser connection when running via AJAX in the preloader.
442 *
443 * @param bool $dont_close_browser_connection The current value of the flag to not close the browser connection. Default is false.
444 *
445 * @return bool
446 */
447 public function preloader_dont_close_browser_connection($dont_close_browser_connection) {
448 if ($dont_close_browser_connection) return $dont_close_browser_connection;
449
450 // In WooCommerce, when the stock is updated for a product or a variation, it triggers an AJAX request in the background.
451 // In this case, we want to avoid closing the browser connection in the preloader, to allow the product edit page to receive the response.
452 if (did_action('woocommerce_variation_set_stock') || did_action('woocommerce_product_set_stock')) {
453 return true;
454 }
455 return false;
456 }
457
458 /**
459 * Triggered by wpo_cache_config_updated.
460 *
461 * @param array $config
462 */
463 public function cache_config_updated($config) {
464 // delete front page form cache if defined in the settings
465 if (is_array($config['cache_exception_urls']) && in_array('/', $config['cache_exception_urls'])) {
466 WPO_Page_Cache::delete_cache_by_url(home_url());
467 WPO_Page_Cache::instance()->file_log("Cache for homepage has been purged due to changes in the cache configurations, triggered by action: " . current_filter());
468 }
469 }
470
471 /**
472 * Returns an instance of the current class, creates one if it doesn't exist
473 *
474 * @return self
475 */
476 public static function instance() {
477 if (empty(self::$instance)) {
478 self::$instance = new self();
479 }
480 return self::$instance;
481 }
482 }
483
484 endif;
485