PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.18
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.18
4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.0.30 4.0.29 4.0.28 4.0.27 4.0.26 4.0.24 4.0.25 4.0.23 4.0.22 4.0.21 4.0.19 4.0.18 4.0.17 4.0.16 1.9.3 All 173 releases
searchpro / inc / cache / class-berqcache.php

class-berqcache.php in BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript 4.1.18, at inc/cache/class-berqcache.php

1,072 lines 37.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH'))
3 exit;
4
5 use BerqWP\BerqWP;
6
7 if (!class_exists('berqCache')) {
8 class berqCache
9 {
10 public static $instance = null;
11
12 function __construct()
13 {
14
15 $this->process_migration();
16
17 add_filter('berqwp_ignored_urls_params', [$this, 'ingore_tracking_params']);
18
19 add_action('template_redirect', [$this, 'queue_page'], 11);
20
21 if (!isset($_GET['bwp_preload'])) {
22 add_action('wp', [$this, 'html_cache'], 2);
23 }
24
25 // Clear page cache on update
26 add_action('save_post', [$this, 'clear_cache_on_post_update'], 10, 3);
27
28 // // Automatic cache warmup
29 // // add_action('init', [$this, 'warmup_home']);
30 // add_action('berqwp_flush_all_cache', [$this, 'warmup_home']);
31 // add_action('warmup_cache_quickly', 'warmup_cache_by_url');
32 // add_action('bwp_warmup_sitemap', [$this, 'warmup_sitemap']);
33 // add_action('berqwp_flush_all_cache', [$this, 'request_warmup_cache']);
34 // add_action('berqwp_cache_warmup', [$this, 'request_warmup_cache']);
35
36 // Delete cache files when the mode changes
37 // add_action('berqwp_before_update_optimization_mode', [$this, 'delete_cache_files']);
38 add_action('berqwp_on_update_optimization_mode', [$this, 'delete_cache_files']);
39
40 // Reverse proxy cache support
41 add_action('berqwp_stored_page_cache', [$this, 'flush_reverse_proxy_cache']);
42 add_action('berqwp_flush_all_cache', ['berqReverseProxyCache', 'flush_all']);
43 add_action('berqwp_flush_page_cache', [$this, 'flush_reverse_proxy_cache']);
44
45 // Clear cache warmup lock after storing the cache
46 add_action('berqwp_stored_page_cache', 'bwp_clear_warmup_lock');
47
48 // Log recently optimized pages
49 add_action('berqwp_stored_page_cache', 'bwp_log_recently_optimized_page', 20, 1);
50 add_action('berqwp_flush_all_cache', 'bwp_clear_recently_optimized_log');
51 add_action('berqwp_flush_page_cache', 'bwp_remove_recently_optimized_entry', 10, 1);
52
53 add_action('init', [$this, 'bypass_cache']);
54
55 // add_action('init', [$this, 'bypass_cache']);
56
57 // Flush cache when there's a new comment
58 add_action('wp_set_comment_status', [$this, 'handle_new_comment'], 10, 2);
59
60 // Store cache without any need for the rest api
61 add_action('init', 'bwp_webhook');
62
63 // Handle request new cache request
64 add_action('init', 'bwp_handle_request_cache');
65
66 // Flush cache when theme changes
67 add_action('switch_theme', [$this, 'delete_cache_files']);
68
69 // Flush critical css when theme changes
70 add_action('switch_theme', [$this, 'purge_critical_css_cache']);
71
72 // Flush critical css when custimizer additional css is updated
73 add_action('customize_save_css', [$this, 'purge_critical_css_cache']);
74
75 // Request cache when a post is published
76 // clear_cache_on_post_update and handle_post_status_update almost do the same thing
77 // add_action( 'transition_post_status', [$this, 'handle_post_status_update'], 10, 3 );
78
79 add_action('send_headers', [$this, 'brust_cache_for_loggedin']);
80
81 // Purge home page when new post is published
82 add_action('transition_post_status', [$this, 'purge_home'], 10, 3);
83
84 // flush cloudflare edge cache
85 add_action('berqwp_stored_page_cache', [$this, 'flush_cf_page']);
86 add_action('berqwp_flush_page_cache', [$this, 'flush_cf_page']);
87 add_action('berqwp_flush_all_cache', 'bwp_cf_flush_all');
88 add_action('berqwp_on_update_sandbox_mode', 'bwp_cf_flush_all');
89 add_action('berqwp_deactivate_plugin', 'bwp_cf_delete_rules');
90 add_action('berqwp_activate_plugin', [$this, 'check_cf_rules']);
91
92 // Apache htaccess cache rules (skip on LiteSpeed — remove any existing rules)
93 add_action('berqwp_activate_plugin', 'bwp_write_htaccess_rules');
94 add_action('berqwp_flush_all_cache', 'bwp_write_htaccess_rules');
95 add_action('berqwp_flush_all_cache', 'bwp_delete_php_files_in_cache');
96 add_action('berqwp_deactivate_plugin', 'bwp_remove_htaccess_rules');
97
98 // Keep the dropin's cached permalink convention in sync with WordPress
99 add_action('init', 'bwp_sync_permalink_config');
100 add_action('update_option_permalink_structure', 'bwp_sync_permalink_config');
101 add_action('berqwp_activate_plugin', 'bwp_sync_permalink_config');
102
103 if (bwp_is_openlitespeed_server()) {
104 add_action('berqwp_activate_plugin', 'bwp_remove_htaccess_rules');
105 }
106
107 add_action('berqwp_on_update_sandbox_mode', 'bwp_sync_htaccess_on_sandbox_change');
108
109 // Clear queue list on cloud
110 add_action('berqwp_deactivate_plugin', 'berqwp_clear_cache_queue');
111
112 // Edit post type list custom links
113 $bwp_post_types = get_option('berqwp_optimize_post_types');
114
115 if (is_admin() && !empty($bwp_post_types)) {
116 foreach ($bwp_post_types as $post_type) {
117 add_filter("{$post_type}_row_actions", [$this, 'post_type_action_links'], 10, 2);
118 }
119 }
120
121 // detect when theme/plugin is installed or updated
122 add_action( 'upgrader_process_complete', [$this, 'process_updates'], 10, 2 );
123
124 // handle menu update
125 add_action( 'wp_update_nav_menu', [$this, 'handle_menu_update'], 10, 2 );
126
127 // handle customizer save
128 add_action( 'customize_save', [$this, 'handle_customizer_update'] );
129
130 }
131
132 static function getInstance() {
133
134 if (self::$instance === null) {
135 $instance = new berqCache();
136 self::$instance = $instance;
137 return $instance;
138 }
139
140 return self::$instance;
141 }
142
143 function handle_customizer_update( $wp_customize ) {
144
145 global $berq_log;
146 $berq_log->info("Customizer updated");
147
148 $this->flush_all();
149
150 }
151
152 function handle_menu_update( $menu_id, $menu_data = null ) {
153
154 global $berq_log;
155 $berq_log->info("Site menu updated");
156
157 $this->flush_all();
158
159 }
160
161 function flush_all() {
162
163 global $berq_log;
164 $berq_log->info("Flushing everything");
165
166 if (berqwp_can_use_cloud()) {
167
168 // flush critical css
169 $this->purge_critical_css_cache();
170
171 // stale cdn
172 self::stale_cloud_assets();
173
174 }
175
176 // clear cache
177 $this->delete_cache_files();
178 }
179
180 function process_updates( WP_Upgrader $upgrader, array $hook_extra ) {
181
182 if (!berqwp_can_use_cloud()) {
183 return;
184 }
185
186 global $berq_log;
187 $berq_log->info("Processing updates");
188
189 self::stale_cloud_assets();
190 $this->purge_critical_css_cache();
191
192 // trigger cache warmup
193 do_action('berqwp_cache_warmup');
194
195 }
196
197 static function stale_cloud_assets() {
198 if (!berqwp_can_use_cloud()) {
199 return;
200 }
201
202 global $berq_log;
203 $berq_log->info("Marking cloud assets stale");
204
205 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
206 $parsed_url = wp_parse_url(home_url());
207 $domain = $parsed_url['host'];
208
209 // mark cdn assets stale
210 $berqwp->cdn_stale_assets($domain);
211
212 // flush all critical css cache
213 $berqwp->purge_critilclcss($domain);
214 }
215
216 function process_migration() {
217 $raw_home = get_option('home');
218
219 if (defined('WP_HOME')) {
220 $raw_home = WP_HOME;
221 }
222
223 // detect migration when home changes
224 if (get_option('berqwp_raw_home', $raw_home) !== $raw_home) {
225 global $berq_log;
226 $berq_log->info('Raw home url change detected, flushing all cache');
227
228 if (get_option('berqwp_raw_home', $raw_home) !== null) {
229 $berq_log->info('Home url change detected, flushing all cache');
230 }
231
232 // regenerate site id
233 $berqconfigs = berqConfigs::getInstance();
234 $blog_id = get_current_blog_id();
235 $network_id = function_exists('get_current_network_id') ? get_current_network_id() : 1;
236 $siteurl = get_option('siteurl');
237 $site_id = md5("berqwp|$network_id|$blog_id|$siteurl");
238
239 $berqconfigs->update_configs(['site_id' => $site_id, 'secret' => '']);
240
241
242 // Flush critical css
243 $parsed_url = wp_parse_url(home_url());
244 $domain = $parsed_url['host'];
245 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
246 $berqwp->purge_critilclcss($domain);
247
248 // Flush page cache
249 $this->delete_cache_files();
250
251 update_option('berqwp_raw_home', $raw_home);
252 delete_transient('berq_lic_response_cache');
253 delete_transient('berqwp_lic_response_cache');
254 delete_transient('berqwp_warmup_running');
255 }
256 }
257
258 function post_type_action_links($actions, $post)
259 {
260
261 $page_url = get_permalink($post->ID);
262 $url = wp_nonce_url(admin_url('admin-post.php?action=berq_purge_page&uri=' . urlencode($page_url)), 'berq_purge_page_action');
263
264 $actions['berqwp_flush_cache'] = '<a href="' . esc_url($url) . '">Flush Cache</a>';
265
266 $url = wp_nonce_url(admin_url('admin-post.php?action=berq_request_cache&uri=' . urlencode($page_url)), 'berq_request_cache_action');
267
268 $actions['berqwp_force_cache'] = '<a href="' . esc_url($url) . '">Force Cache</a>';
269
270 return $actions;
271 }
272
273 function check_cf_rules()
274 {
275 if (!empty(get_option('berqwp_cf_creden'))) {
276 $email = get_option('berqwp_cf_creden')['email'];
277 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
278 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
279 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
280
281 if ($berqCloudflareAPIHandler->verify_credentials()) {
282 $berqCloudflareAPIHandler->add_rule();
283 $berqCloudflareAPIHandler->purge_all_cache();
284 return;
285 }
286
287 delete_option('berqwp_cf_creden');
288 }
289 }
290
291 function flush_author_cache($post_id)
292 {
293 // Bail out on autosaves and revisions
294 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
295 return;
296 if (wp_is_post_revision($post_id))
297 return;
298
299 $post = get_post($post_id);
300 if (!$post)
301 return;
302
303 global $berq_log;
304
305 // Get current author ID
306 $current_author_id = $post->post_author;
307
308 // Get original author ID before any potential changes
309 $original_post = wp_get_post_revision($post_id);
310 $original_author_id = $original_post ? $original_post->post_author : $current_author_id;
311
312 // Collect all author IDs that need flushing
313 $author_ids = array_unique([$current_author_id, $original_author_id]);
314
315 foreach ($author_ids as $author_id) {
316 // Skip invalid author IDs
317 if (!$author_id || !get_userdata($author_id))
318 continue;
319
320 // Get author archive URL
321 $author_link = get_author_posts_url($author_id);
322 if (!is_wp_error($author_link) && !empty($author_link)) {
323 $berq_log->info("Purging author cache: $author_link");
324 self::purge_page($author_link);
325
326 // Handle translations for multilingual author archives
327 $translation_urls = apply_filters('berqwp_page_translation_urls', [], $author_link);
328 if (!empty($translation_urls)) {
329 foreach ($translation_urls as $url) {
330 $berq_log->info("Purging author translation cache: $url");
331 self::purge_page($url);
332 }
333 }
334 }
335 }
336 }
337
338 function flush_post_taxonomy_cache($post_id)
339 {
340 // Bail out on autosaves and revisions.
341 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
342 return;
343 }
344 if (wp_is_post_revision($post_id)) {
345 return;
346 }
347
348 $post = get_post($post_id);
349 if (!$post) {
350 return;
351 }
352
353 global $berq_log;
354
355 // Get all taxonomies registered for this post type.
356 $taxonomies = get_object_taxonomies($post->post_type);
357 if (!empty($taxonomies)) {
358 // Clear the object term cache for the post.
359 clean_object_term_cache($post_id, $taxonomies);
360
361 // Loop through each taxonomy.
362 foreach ($taxonomies as $taxonomy) {
363
364 if (!in_array($taxonomy, get_option('berqwp_optimize_taxonomies'))) {
365 continue;
366 }
367
368 // Get all terms for this post in the current taxonomy.
369 $terms = get_the_terms($post_id, $taxonomy);
370 if (empty($terms) || is_wp_error($terms)) {
371 continue;
372 }
373
374 foreach ($terms as $term) {
375 // Clear the term cache for the original term.
376 clean_object_term_cache($term->term_id, $taxonomy);
377
378 // Collect terms to purge: original term and its ancestors (if hierarchical)
379 $terms_to_purge = array($term);
380
381 // Check if taxonomy is hierarchical to process ancestors
382 if (is_taxonomy_hierarchical($taxonomy)) {
383 $ancestor_ids = get_ancestors($term->term_id, $taxonomy, 'taxonomy');
384 foreach ($ancestor_ids as $ancestor_id) {
385 $ancestor_term = get_term($ancestor_id, $taxonomy);
386 if ($ancestor_term && !is_wp_error($ancestor_term)) {
387 $terms_to_purge[] = $ancestor_term;
388 }
389 }
390 }
391
392 // Process each term (original and ancestors)
393 foreach ($terms_to_purge as $term_to_purge) {
394 $term_link = get_term_link($term_to_purge);
395 if (is_wp_error($term_link)) {
396 continue;
397 }
398
399 $berq_log->info("Purging cache for term $term_link");
400 self::purge_page($term_link);
401
402 // Handle translations
403 $translation_urls = apply_filters('berqwp_page_translation_urls', array(), $term_link);
404 if (!empty($translation_urls)) {
405 foreach ($translation_urls as $url) {
406 $berq_log->info("Purging taxonomy translation for $url");
407 self::purge_page($url);
408 }
409 }
410 }
411 }
412 }
413 }
414 }
415
416 function purge_critical_css_cache()
417 {
418 $parsed_url = wp_parse_url(home_url());
419 $domain = $parsed_url['host'];
420 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
421 $berqwp->purge_critilclcss($domain);
422 }
423
424 function flush_cf_page($slug = '')
425 {
426 if (empty($slug)) {
427 $slug = '/';
428 }
429 $url = home_url($slug);
430 bwp_cf_flush_page($url);
431 }
432
433 function purge_home($new_status, $old_status, $post)
434 {
435
436 $can_flush_cache = apply_filters('berqwp_can_flush_home_cache_on_post_update', true);
437
438 if (!$can_flush_cache) {
439 return;
440 }
441
442 if ($new_status == 'draft' && $old_status == 'draft') {
443 return;
444 }
445
446 if ($new_status == 'auto-draft') {
447 return;
448 }
449
450 if ($new_status == 'trash') {
451 return;
452 }
453
454 if ($new_status == 'publish' && $old_status == 'publish') {
455 return;
456 }
457
458 // if ($old_status == 'new' && $new_status == 'draft') {
459 // return;
460 // }
461
462 // if statement skips when scheduled post publishes
463 // with admin wp cron request
464 // if (!is_admin()) {
465 // $berq_log->info('Trasition post status admin' . ' ' . $new_status);
466 // return;
467 // }
468
469 $post_types = apply_filters('berqwp_purge_home_post_types', ['post']);
470
471 if (in_array($post->post_type, $post_types)) {
472 global $berq_log;
473 $berq_log->info('Purging homepage. Triggered by post type: ' . $post->post_type . ' ' . $old_status . ' ' . $new_status);
474 $home_url = home_url('/');
475 self::purge_page($home_url);
476 warmup_cache_by_url($home_url, true);
477 }
478
479 $blog_page_id = get_option('page_for_posts');
480
481 if ($blog_page_id) {
482
483 $blog_url = get_permalink($blog_page_id);
484
485 global $berq_log;
486 $berq_log->info('Purging blog page: ' . $blog_url);
487
488 self::purge_page($blog_url);
489 warmup_cache_by_url($blog_url);
490 }
491 }
492
493 function brust_cache_for_loggedin()
494 {
495 if (is_user_logged_in()) {
496 // Add Vary: Cookie header to differentiate cached versions for logged-in users
497 header('Vary: Accept-Encoding, Cookie');
498 // header("Cache-Control: no-cache, no-store, must-revalidate");
499 // header("Pragma: no-cache");
500 // header("Expires: 0");
501 }
502 }
503
504 function handle_post_status_update($new_status, $old_status, $post)
505 {
506 // Get the post types that should be optimized from BerqWP settings
507 $post_types = get_option('berqwp_optimize_post_types');
508
509 // Get the published post URL
510 $post_url = get_permalink($post);
511
512 // Only proceed if the post is being published (from any status to 'publish')
513 if ('publish' === $new_status && 'publish' !== $old_status) {
514
515 // Check if the post type is in the list of types to be optimized
516 if (in_array($post->post_type, $post_types)) {
517
518 // We need path with query parameters
519 $parsed_url = parse_url($post_url);
520 $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
521 $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
522 $path_with_query = $path . $query;
523
524 if (berqwp_is_slug_excludable($path_with_query)) {
525 return;
526 }
527
528 global $berq_log;
529 $berq_log->info("Post status updated $post_url");
530
531 if (bwp_can_optimize_page_url($post_url)) {
532 warmup_cache_by_url($post_url);
533 }
534
535 self::purge_page($post_url, true);
536
537 $this->flush_post_taxonomy_cache($post->ID);
538 $this->flush_author_cache($post->ID);
539 }
540 }
541 }
542
543 public static function delete_page_cache_files($page_url)
544 {
545 $cache_file = bwp_get_cache_dir() . bwp_build_cache_path($page_url) . '/index.html.gz';
546
547 if (file_exists($cache_file)) {
548 unlink($cache_file);
549 }
550 }
551
552 public static function purge_page($page_url, $flush_criticalcss = false)
553 {
554
555 if (!berqwp_can_use_cloud()) {
556 $flush_criticalcss = false;
557 }
558
559 $page_url = strtolower($page_url);
560
561 // $page_path = bwp_intersect_str(home_url(), $page_path);
562 $slug = bwp_url_into_path($page_url);
563
564 self::delete_page_cache_files($page_url);
565
566 do_action('berqwp_flush_page_cache', $slug);
567
568 if ($flush_criticalcss) {
569 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
570 $berqwp->purge_criticlecss_url($page_url);
571 }
572
573 bwp_cf_flush_page(home_url($slug));
574 }
575
576 function handle_new_comment($comment_id, $comment_status)
577 {
578 if ($comment_status === 'approve') {
579 $comment = get_comment($comment_id);
580 $post_id = $comment->comment_post_ID;
581 $post_url = get_permalink($post_id);
582
583 self::purge_page($post_url);
584
585 global $berq_log;
586 $berq_log->info("New comment added, deleting cache for $post_url");
587 }
588 }
589
590 function bypass_cache()
591 {
592 if (
593 isset($_GET['nocache'])
594 || isset($_GET['bwp_preload'])
595 // || isset($_GET['creating_cache'])
596 || berqDetectCrawler::is_crawler() ||
597 (get_option('berqwp_enable_sandbox') == 1 && !isset($_GET['berqwp']) && !is_admin() && !isset($_POST))
598 ) {
599
600 add_filter('berqwp_bypass_cache', function () {
601 return true;
602 });
603
604 // if (berqReverseProxyCache::is_reverse_proxy_cache_enabled()) {
605 berqReverseProxyCache::bypass();
606 // }
607
608 }
609 }
610
611 function flush_reverse_proxy_cache($slug = '/.*')
612 {
613
614 if (empty($slug)) {
615 $slug = '/';
616 }
617
618 // if ($slug == '/') {
619 // $slug = '';
620 // }
621
622 $page_url = home_url($slug);
623 $host = !empty($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
624 berqReverseProxyCache::purge_cache($page_url);
625 }
626
627 function ingore_tracking_params($tracking_params)
628 {
629 $tracking_params = array_merge($tracking_params, ignoreParams::$query_params);
630 return $tracking_params;
631 }
632
633 function clear_cache_on_post_update($post_id, $post, $update)
634 {
635
636 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
637 return;
638 }
639
640 // if (!$update) {
641 // return;
642 // }
643
644 $can_flush_cache = apply_filters('berqwp_can_flush_cache_on_post_update', true);
645
646 if (!$can_flush_cache) {
647 return;
648 }
649
650 // If this is just a revision, don't run the function.
651 if (wp_is_post_revision($post_id)) {
652 return;
653 }
654
655 // prevents scheduled post public
656 // via wp cron
657 // if (!is_admin()) {
658 // return;
659 // }
660
661 $post_type = get_post_type($post_id);
662 $post_url = get_permalink($post_id);
663
664 // We need path with query parameters
665 $parsed_url = parse_url($post_url);
666 $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
667 $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
668 $path_with_query = $path . $query;
669
670 if (berqwp_is_slug_excludable($path_with_query)) {
671 return;
672 }
673
674 // Potentially cachable post types
675 $cachable_post_type_names = get_post_types(array(
676 'public' => true,
677 ), 'names');
678 unset($cachable_post_type_names['attachment']);
679
680 if (!in_array($post_type, $cachable_post_type_names)) {
681 return;
682 }
683
684 global $berq_log;
685 $berq_log->info("Post updated [Post type - $post_type]: $post_url $post->post_status");
686
687 self::purge_page($post_url, true);
688
689 if (bwp_can_optimize_page_url($post_url)) {
690 warmup_cache_by_url($post_url);
691 }
692
693 $translation_urls = apply_filters('berqwp_page_translation_urls', [], $post_url);
694
695 if (!empty($translation_urls)) {
696 foreach ($translation_urls as $url) {
697
698 $berq_log->info("Purging translation for $post_url");
699 self::purge_page($url, true);
700 }
701 }
702
703 $this->flush_post_taxonomy_cache($post_id);
704 $this->flush_author_cache($post_id);
705 }
706
707 function flush_cdn()
708 {
709 $parsed_url = wp_parse_url(home_url());
710 $domain = $parsed_url['host'];
711
712 $args = [
713 'timeout' => 30,
714 'sslverify' => false,
715 'body' => ['flush_cdn' => $domain, 'license_key' => berqwp_get_license_key()]
716 ];
717
718 wp_remote_post('https://boost.berqwp.com/photon/', $args);
719
720 // Flush cache
721 $this->delete_cache_files();
722
723 // clear local static files
724
725 $dir = optifer_cache . '/static/';
726
727 if (! is_dir($dir)) {
728 return;
729 }
730
731 $files = glob(rtrim($dir, '/') . '/*');
732
733 foreach ($files as $file) {
734 if (is_file($file)) {
735 unlink($file);
736 }
737 }
738
739 update_option('berqwp_uploaded_assets', [], false);
740
741 do_action('berqwp_purge_cdn');
742 }
743
744 function delete_cache_files($flush_all_sites = false)
745 {
746 global $berq_log;
747 $berq_log->info("Flushing all cache.");
748
749 // Define the cache directory
750 $cache_directory = bwp_get_cache_dir();
751
752 if (is_multisite() && $flush_all_sites) {
753 $cache_directory = optifer_cache . '/html/';
754 }
755
756 // Delete all cache files within the directory
757 berqwp_unlink_recursive($cache_directory);
758
759 // Delete external CSS cache
760 $external_cache_dir = optifer_cache . 'external/';
761 if (is_dir($external_cache_dir)) {
762 berqwp_unlink_recursive($external_cache_dir);
763 }
764
765 delete_transient('berqwp_warmup_running');
766 delete_transient('cache_warmup_in_progress');
767 delete_transient('berqwp_doing_cache_warmup');
768
769 // Remove require cache warning
770 update_option('bwp_require_flush_cache', 0);
771
772 do_action('berqwp_flush_all_cache');
773 }
774
775 static function should_cache() {
776 // Check if the current user is logged in and only allow GET requests
777 if (is_user_logged_in() || $_SERVER['REQUEST_METHOD'] !== 'GET') {
778 return false;
779 }
780
781 if (isset($_GET['creating_cache'])) {
782 return false;
783 }
784
785 if (is_admin() || is_preview() || is_404() || is_search()) {
786 return false;
787 }
788
789 if (defined('DOING_CRON') && DOING_CRON) {
790 return false;
791 }
792
793 if (defined('DOING_AJAX') && DOING_AJAX) {
794 return false;
795 }
796
797 if (defined('REST_REQUEST') && REST_REQUEST) {
798 return false;
799 }
800
801 if (php_sapi_name() === 'cli' || defined('WP_CLI')) {
802 return false;
803 }
804
805 if (!bwp_is_webpage()) {
806 return false;
807 }
808
809 if (!bwp_pass_cookie_requirement()) {
810 return false;
811 }
812
813 $berqconfigs = berqConfigs::getInstance();
814 $configs = $berqconfigs->get_configs();
815
816 if (empty($configs['optimization_method'])) {
817 return false;
818 }
819
820 if (berqwp_can_use_cloud()) {
821
822 if (empty(berqwp_get_license_key())) {
823 return false;
824 }
825
826 // if (!bwp_pass_account_requirement()) {
827 // return false;
828 // }
829
830 }
831
832
833 $bypass_cache = apply_filters('berqwp_bypass_cache', false);
834
835 if ($bypass_cache) {
836 return false;
837 }
838
839 return true;
840 }
841
842 static function get_page_url() {
843 $page_url = bwp_get_request_url();
844
845 // For sitemaps
846 if (strpos($page_url, '.xml') !== false || strpos($page_url, '.xsl') !== false) {
847 return;
848 }
849
850 // Return if page is excluded from cache
851 $current_page_url = bwp_get_request_url();
852
853 if (strpos($current_page_url, '?') !== false) {
854 $current_page_url = explode('?', $current_page_url)[0];
855 }
856
857 if (berqwp_is_page_url_excluded($current_page_url)) {
858 return;
859 }
860
861 // Remove ignored params from the slug
862 // $slug = berqwp_remove_ignore_params($slug_uri);
863 $page_url = berqwp_remove_ignore_params($page_url);
864
865 if (get_option('berqwp_enable_sandbox') == 1 && isset($_GET['berqwp'])) {
866 $page_url = explode('?berqwp', $page_url)[0];
867 } elseif (get_option('berqwp_enable_sandbox') == 1 && !isset($_GET['creating_cache'])) {
868 return false;
869 }
870
871 if (berqwp_is_slug_excludable($page_url)) {
872 return false;
873 }
874
875 return $page_url;
876 }
877
878 static function queue_page($page_url = null) {
879
880
881 if (empty($page_url)) {
882
883 if (!self::should_cache()) {
884 return;
885 }
886
887 $page_url = self::get_page_url();
888 }
889
890 if (empty($page_url)) {
891 return false;
892 }
893
894 // Disable cache for unknown query parameters
895 if (strpos($page_url, '?') !== false) {
896 return false;
897 }
898
899 if (!berqwp_can_use_cloud()) {
900 return false;
901 }
902
903 $status_code = http_response_code();
904
905 if ($status_code !== 200) {
906 return false;
907 }
908
909 // berqHeartbeat::add_queue($page_url);
910 warmup_cache_by_url($page_url);
911 }
912
913 static function miss_cache_headers() {
914 header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
915 header('CDN-Cache-Control: no-store', true); // Cloudflare
916 header('Surrogate-Control: no-store', true); // Varnish
917 header('X-Accel-Expires: 0', true); // Nginx FastCGI
918 header("X-BerqWP-Cache: MISS");
919 }
920
921 function html_cache()
922 {
923
924 // Bypass cache for Photon
925 if (!empty($_COOKIE['berqwpnocache'])) {
926 self::miss_cache_headers();
927 return;
928 }
929
930 if (!self::should_cache()) {
931 self::miss_cache_headers();
932 return;
933 }
934
935 $page_url = self::get_page_url();
936
937 if (empty($page_url)) {
938 self::miss_cache_headers();
939 return;
940 }
941
942 // Disable cache for unknown query parameters
943 if (strpos($page_url, '?') !== false) {
944 self::miss_cache_headers();
945 return;
946 }
947
948 // Redirect non-canonical URLs (wrong trailing slash) the same way WordPress would,
949 // before ever touching the cache — works for both trailing-slash and non-trailing-slash
950 // permalink structures.
951 $canonical_url = bwp_canonicalize_page_url($page_url);
952 if ($canonical_url !== $page_url) {
953 wp_safe_redirect($canonical_url, 301);
954 exit();
955 }
956
957 if (is_singular()) {
958 $post_type = get_post_type();
959
960 if (empty($post_type) || !in_array($post_type, get_option('berqwp_optimize_post_types'))) {
961 self::miss_cache_headers();
962 return;
963 }
964 } elseif (is_archive()) {
965 $queried_object = get_queried_object();
966
967 if ($queried_object instanceof WP_Term && !empty($queried_object->taxonomy)) {
968 $current_taxonomy = $queried_object->taxonomy;
969
970 if (!in_array($current_taxonomy, get_option('berqwp_optimize_taxonomies'))) {
971 self::miss_cache_headers();
972 return;
973 }
974 }
975 }
976
977
978 $status_code = http_response_code();
979
980 if ($status_code !== 200) {
981 self::miss_cache_headers();
982 return;
983 }
984
985 $berqconfigs = berqConfigs::getInstance();
986 $configs = $berqconfigs->get_configs();
987 $cache_key = md5($page_url);
988 $cache_directory = bwp_get_cache_dir();
989
990 $cache_file = $cache_directory . bwp_build_cache_path($page_url) . '/index.html.gz';
991 $cache_max_life = file_exists($cache_file) ? @filemtime($cache_file) + $configs['cache_lifespan'] : null;
992
993 if (file_exists($cache_file) && $cache_max_life > time()) {
994 header_remove('Content-Encoding');
995
996 $lastModified = filemtime($cache_file);
997 $etag = md5_file($cache_file);
998 header('ETag: ' . $etag);
999 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');
1000 header('X-File-Size: ' . filesize($cache_file), true);
1001 header('Content-Type: text/html; charset=utf-8');
1002 header("X-served: WP Hook");
1003 header('Vary: Accept-Encoding, Cookie');
1004 header("X-BerqWP-Cache: HIT");
1005
1006 if ((isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified) || (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag)) {
1007 // The client's cache is still valid based on Last-Modified, respond with a 304 Not Modified
1008 header('HTTP/1.1 304 Not Modified');
1009 // header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
1010 // header("Expires: 0");
1011 // header('Cache-Control: no-cache, must-revalidate');
1012
1013 header('Cache-Control: public, max-age=600, s-maxage=2592000, stale-while-revalidate=86400', true);
1014 header('cdn-cache-control: max-age=2592000');
1015 header('Cache-Tag: ' . wp_parse_url(home_url())['host']);
1016
1017 exit();
1018 }
1019
1020 // Prevent PHP/Apache from re-compressing the already-gzipped cache file
1021 if (ini_get('zlib.output_compression')) {
1022 ini_set('zlib.output_compression', 'Off');
1023 }
1024
1025 while (ob_get_level()) {
1026 ob_end_clean();
1027 }
1028
1029 header('Cache-Control: public, max-age=600, s-maxage=2592000, stale-while-revalidate=86400', true);
1030 header('cdn-cache-control: max-age=2592000');
1031 header('Cache-Tag: ' . wp_parse_url(home_url())['host']);
1032 // header("Content-Security-Policy: script-src 'self' blob: 'unsafe-inline'");
1033 header('Content-Encoding: gzip', true);
1034 header('Content-Length: ' . filesize($cache_file), true);
1035 readfile($cache_file);
1036 exit();
1037 }
1038
1039 self::miss_cache_headers();
1040
1041 self::purge_page($page_url);
1042
1043 if (!berqwp_can_use_cloud()) {
1044 try {
1045
1046 $berqPageOptimizer = new berqPageOptimizer();
1047 $berqPageOptimizer->set_slug(bwp_url_into_path($page_url));
1048 $berqPageOptimizer->set_page($page_url);
1049 $berqPageOptimizer->start_cache();
1050 unset($berqPageOptimizer);
1051
1052 } catch (Exception $e) {
1053 global $berq_log;
1054 $berq_log->error($e->getMessage());
1055 return;
1056 } catch (Throwable $e) {
1057 global $berq_log;
1058 $berq_log->error($e->getMessage());
1059 return;
1060 }
1061
1062 }
1063
1064 return;
1065 }
1066
1067 }
1068
1069 // $cache = new berqCache();
1070 $cache = berqCache::getInstance();
1071 }
1072