PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.19
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.19
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 / helper-functions.php

helper-functions.php in BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript 4.1.19, at inc/helper-functions.php

2,479 lines 75.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use BerqWP\BerqWP;
4 use BerqWP_Deps\voku\helper\HtmlDomParser;
5
6 if (!defined('ABSPATH'))
7 exit;
8
9 // ==========================================
10 // Multisite Network Option Helpers
11 // ==========================================
12
13 function berqwp_get_license_key() {
14 if (function_exists('is_multisite') && is_multisite()) {
15 return get_site_option('berqwp_license_key', '');
16 }
17 return get_option('berqwp_license_key', '');
18 }
19
20 function berqwp_remove_license_key() {
21 if (function_exists('is_multisite') && is_multisite()) {
22 return delete_site_option('berqwp_license_key');
23 }
24 return delete_option('berqwp_license_key');
25 }
26
27 function berqwp_update_license_key($key) {
28 if (function_exists('is_multisite') && is_multisite()) {
29 return update_site_option('berqwp_license_key', $key);
30 }
31 return update_option('berqwp_license_key', $key);
32 }
33
34 function berqwp_delete_license_key() {
35 if (function_exists('is_multisite') && is_multisite()) {
36 return delete_site_option('berqwp_license_key');
37 }
38 return delete_option('berqwp_license_key');
39 }
40
41 function berqwp_get_network_option($key, $default = false) {
42 if (function_exists('is_multisite') && is_multisite()) {
43 return get_site_option($key, $default);
44 }
45 return get_option($key, $default);
46 }
47
48 function berqwp_update_network_option($key, $value) {
49 if (function_exists('is_multisite') && is_multisite()) {
50 return update_site_option($key, $value);
51 }
52 return update_option($key, $value);
53 }
54
55 function berqwp_delete_network_option($key) {
56 if (function_exists('is_multisite') && is_multisite()) {
57 return delete_site_option($key);
58 }
59 return delete_option($key);
60 }
61
62 function berqwp_is_network_activated() {
63 if (!function_exists('is_multisite') || !is_multisite()) {
64 return false;
65 }
66 if (function_exists('is_plugin_active_for_network')) {
67 return is_plugin_active_for_network('searchpro/berqwp.php');
68 }
69 return false;
70 }
71
72 function berqwp_is_multisite() {
73 return function_exists('is_multisite') && is_multisite();
74 }
75
76 function berqwp_is_license_managed_by_network() {
77 return berqwp_is_multisite() && berqwp_is_network_activated();
78 }
79
80 function berqwp_is_license_active() {
81 return !empty(berqwp_get_license_key());
82 }
83
84 function berqwp_activate_single_site() {
85 if (empty(berqwp_get_license_key())) {
86 set_transient('berqwp_redirect', true, 1);
87 }
88 update_option('berqwp_sync_addons', true);
89 do_action('berqwp_activate_plugin');
90 }
91
92 function berqwp_is_slug_excludable($slug)
93 {
94 // Allow empty slug
95 // if (empty($slug)) {
96 // return true;
97 // }
98
99 $exclude_items = [
100 "favicon.ico",
101 "moderation-hash=",
102 "elementor_library=",
103 "add_to_wishlist=",
104 "robots.txt",
105 ".php",
106 "run_warmup=",
107 "et-compare-page=",
108 "add_to_compare=",
109 "min_price=",
110 "max_price=",
111 "view_mode=",
112 "view_mode_smart=",
113 "et_columns-count=",
114 "add_to_wishlist=",
115 "et-wishlist-page=",
116 "remove_wishlist=",
117 "stock_status=",
118 "page_id=",
119 "?p=",
120 "add-to-cart=",
121 "remove_item=",
122 "cart_item_key=",
123 "quantity=",
124 "my-account=",
125 "lost-password=",
126 "reset-password=",
127 "order-pay=",
128 "order-received=",
129 "view-order=",
130 "?s=",
131 "&s=",
132 "orderby=",
133 "product_tag=",
134 "product_cat=",
135 "min_price=",
136 "max_price=",
137 "rating=",
138 "filter_",
139 "edd_action=",
140 "download_id=",
141 "bbp_=",
142 "bbp-search=",
143 "&bp_=",
144 "?bp_=",
145 "?action=",
146 "&action=",
147 "gf_page=",
148 "gf_token=",
149 "gform_submit=",
150 "wordfence_logHuman=",
151 "wordfence_lh=",
152 "wordfence_syncAttackData=",
153 "?elementor=",
154 "&elementor=",
155 "?ld_=",
156 "&ld_=",
157 "lesson_id=",
158 "quiz_id=",
159 "wpforms=",
160 "?ref=",
161 "&ref=",
162 "?aff=",
163 "&aff=",
164 "?llms_=",
165 "&llms_=",
166 "lesson_id=",
167 "course_id=",
168 "vc_editable=true",
169 "et_fb=1",
170 "et_fb_edit",
171 "rcp_action=",
172 "member_id=",
173 "give_action=",
174 "donation_id=",
175 "giveDonationFormInIframe=",
176 "tribe-bar-date=",
177 "tribe_eventcategory=",
178 "tribe_paged=",
179 "tribe_organizer=",
180 "tribe_venue=",
181 "job_id=",
182 "job_applications=",
183 "job_alerts=",
184 "mepr-process=",
185 "mepr-transaction=",
186 "mepr_coupon=",
187 "popmake=",
188 "pum_form_sub=",
189 "pum_action=",
190 "ngg_page=",
191 "gallery_id=",
192 "pid=",
193 "wp_simple_pay=",
194 "sp_dont_optimize=",
195 "remove_from_wishlist=",
196 "action=yith-woocompare",
197 "?amp=",
198 "&amp=",
199 "?noamp=",
200 "&noamp=",
201 "subscription_id=",
202 "renewal_order=",
203 "edd_bk=",
204 "wpgmza=",
205 "poll_id=",
206 "pollresult=",
207 "swpquery=",
208 "swpengine=",
209 "monsterinsights=",
210 "defender=",
211 "wdf_scan=",
212 "wc_bookings_field=",
213 "wc_bookings_calendar=",
214 "?na=",
215 "&na=",
216 "?newsletter=",
217 "&newsletter=",
218 "affwp_ref=",
219 "affwp_campaign=",
220 "hmwp_token=",
221 "/page/",
222 "/search/",
223 "?",
224 "/embed",
225 "/view-order/",
226 "/redirect/",
227 "/elementskit-content/",
228 "/checkout/",
229 ];
230
231 $exclude_items = apply_filters('berqwp_exclude_slug_match', $exclude_items);
232
233 foreach ($exclude_items as $item) {
234 if (strpos($slug, $item) !== false) {
235 return true;
236 }
237 }
238
239 return false;
240 }
241
242
243 function berqwp_home_url() {
244 return untrailingslashit(apply_filters('berqwp_site_url', home_url()));
245 }
246
247 function bwp_canonicalize_page_url($url) {
248 $permalink_structure = get_option('permalink_structure');
249
250 if (!$permalink_structure) {
251 return $url;
252 }
253
254 $path = parse_url($url, PHP_URL_PATH) ?? '/';
255
256 if ($path === '/') {
257 return $url;
258 }
259
260 $uses_trailing_slash = substr(rtrim($permalink_structure), -1) === '/';
261 $has_trailing_slash = substr($path, -1) === '/';
262
263 if ($uses_trailing_slash && !$has_trailing_slash) {
264 return trailingslashit($url);
265 }
266
267 if (!$uses_trailing_slash && $has_trailing_slash) {
268 return untrailingslashit($url);
269 }
270
271 return $url;
272 }
273
274 function bwp_sync_permalink_config() {
275 $permalink_structure = get_option('permalink_structure');
276 $trailing_slash = empty($permalink_structure) ? null : (substr(rtrim($permalink_structure), -1) === '/');
277
278 $berqconfigs = berqConfigs::getInstance();
279 $current = $berqconfigs->get_configs();
280
281 if ($current === false || !array_key_exists('permalink_trailing_slash', $current) || $current['permalink_trailing_slash'] !== $trailing_slash) {
282 $berqconfigs->update_configs(['permalink_trailing_slash' => $trailing_slash]);
283 }
284 }
285
286 function berqwp_get_page_params($page_url, $is_forced = false)
287 {
288
289 if (empty($page_url)) {
290 return;
291 }
292
293 $page_slug = bwp_url_into_path($page_url);
294 $key = '';
295 $berqconfigs = berqConfigs::getInstance();
296 $berqwp_configs = $berqconfigs->get_configs();
297
298 $optimization_mode = get_option('berq_opt_mode');
299
300 if ($optimization_mode == 4) {
301 $optimization_mode = 'aggressive';
302 } elseif ($optimization_mode == 3) {
303 $optimization_mode = 'blaze';
304 } elseif ($optimization_mode == 2) {
305 $optimization_mode = 'medium';
306 } elseif ($optimization_mode == 1) {
307 $optimization_mode = 'basic';
308 }
309
310 $home_url = berqwp_home_url();
311
312 // Data to send as POST parameters
313 $post_data = array(
314 'site_id' => $berqwp_configs['site_id'],
315 'license_key' => berqwp_get_license_key(),
316 'page_url' => $page_url,
317 'page_slug' => $page_slug,
318 'site_url' => $home_url,
319 'webp_max_width' => (int) get_option('berqwp_webp_max_width'),
320 'webp_quality' => (int) get_option('berqwp_webp_quality'),
321 'fluid_images' => get_option('berqwp_fluid_images'),
322 'enable_critical_css' => get_option('berqwp_enable_critical_css'),
323 'enable_used_css' => get_option('berqwp_enable_used_css'),
324 'force_include_critical_css'=> get_option('berqwp_force_include_critical_css', []),
325 'exclude_css' => get_option('berqwp_exclude_css', []),
326 'exclude_js' => get_option('berqwp_exclude_js', []),
327 'exclude_third_party_js' => get_option('berqwp_exclude_third_party_js', []),
328 'async_excluded_styles' => get_option('berqwp_async_excluded_styles'),
329 'prerender_link' => get_option('berqwp_prerender_on_hover'),
330 'defer_excluded_js' => get_option('berqwp_defer_excluded_js'),
331 'delay_third_party_scripts' => get_option('berqwp_delay_third_party_scripts'),
332 'lazy_render' => get_option('berqwp_lazy_render'),
333 'img_lazyloading' => get_option('berqwp_image_lazyloading'),
334 'exclude_img_lazy_load' => get_option('berqwp_exclude_lazy_load_images', []),
335 'lazy_load_videos' => get_option('berqwp_lazy_load_videos'),
336 'youtube_lazyloading' => get_option('berqwp_lazyload_youtube_embed'),
337 'preload_yt_poster' => get_option('berqwp_preload_yt_poster'),
338 'js_mode' => get_option('berqwp_javascript_execution_mode'),
339 'key' => $key,
340 // 'interaction_delay' => get_option('berqwp_interaction_delay'),
341 'use_cdn' => get_option('berqwp_enable_cdn'),
342 'opt_mode' => $optimization_mode,
343 'disable_webp' => !get_option('berqwp_enable_webp'),
344 // 'js_css_exclude_urls' => get_option('berq_exclude_js_css', []),
345 'preload_fontfaces' => get_option('berqwp_preload_fontfaces'),
346 'enable_cwv' => get_option('berqwp_enable_cwv'),
347 'preload_cookiebanner' => get_option('berqwp_preload_cookiebanner'),
348 'css_optimization' => get_option('berq_css_optimization'),
349 'js_optimization' => get_option('berq_js_optimization'),
350 'exclude_cdn' => get_option('berq_exclude_cdn', []),
351 'webhook' => true,
352 'version' => BERQWP_VERSION
353 );
354
355 if (!get_option('berqwp_can_use_fluid_images')) {
356 $post_data['fluid_images'] = 0;
357 }
358
359 // if (defined('BERQ_STAGING')) {
360 // $post_data['run_queue'] = 1;
361 // $post_data['doing_queue'] = true;
362 // }
363
364 if ($page_url == home_url('/')) {
365 $is_forced = true;
366 }
367
368 if ($is_forced) {
369 $post_data['force'] = true;
370 }
371
372 return $post_data;
373 }
374
375 function bwp_pass_account_requirement()
376 {
377 global $berq_log;
378 $berqWP = \berqWP::getInstance();
379
380 $license_key = berqwp_get_license_key();
381
382 if (empty($license_key)) {
383 return false;
384 }
385
386 global $berq_log;
387 // $berq_log->info("License key check from bwp_pass_account_requirement function.");
388
389 $key_response = $berqWP->verify_license_key($license_key);
390
391 if (empty($key_response->product_ref)) {
392 return false;
393 }
394
395 if ($key_response->result !== 'success' || $key_response->status !== 'active') {
396 // $berq_log->error("account requirement: license verification failed");
397 return false;
398 }
399
400 if ($key_response->product_ref == 'Free Account' && bwp_cached_pages_count() >= 10) {
401 return false;
402 }
403
404 if ($key_response->product_ref == 'Starter' && bwp_cached_pages_count() >= 100) {
405 return false;
406 }
407
408 return true;
409 }
410
411 function warmup_cache_by_url($page_url, $is_forced = false, $async = false)
412 {
413
414 if (!berqwp_can_use_cloud()) {
415 return;
416 }
417
418 if (empty($page_url)) {
419 return;
420 }
421
422 if (empty(berqwp_get_license_key())) {
423 return;
424 }
425
426 if (berq_is_localhost() && !defined('BERQWP_ALLOW_LOCALHOST')) {
427 return;
428 }
429
430 if (berq_is_localhost() && defined('BERQWP_ALLOW_LOCALHOST') && BERQWP_ALLOW_LOCALHOST === false) {
431 return;
432 }
433
434 $page_url = strtolower($page_url);
435
436 $parsed_site_url = wp_parse_url(home_url());
437 $parsed_page_url = wp_parse_url($page_url);
438
439 if ($parsed_page_url['host'] !== $parsed_site_url['host']) {
440 return;
441 }
442
443 // Avoid caching file URLs e.g .php
444 // Allow .html
445 if (berqwp_is_file_url($page_url)) {
446 return;
447 }
448
449 // We don't want to cache page URL with query params
450 if (strpos($page_url, '?') !== false) {
451 return;
452 }
453
454 // Return if page url or page path (slug) is excluded
455 if (!bwp_can_optimize_page_url($page_url)) {
456 return;
457 }
458
459 global $berq_log;
460 $berq_log->info('warming page: ' . $page_url);
461
462 // Prepare post data for this page
463 $post_data = berqwp_get_page_params($page_url, $is_forced);
464
465 $berqwp = new BerqWP(get_option('berqwp_license_key'), null, optifer_cache);
466 $timeout = 30;
467
468 if ($async) {
469 $timeout = 1;
470 }
471
472 $berqwp->request_cache($post_data, $timeout);
473 }
474
475 function bwp_is_home_cached()
476 {
477 $cache_directory = bwp_get_cache_dir();
478 $cache_file = $cache_directory . bwp_build_cache_path(bwp_admin_home_url('/')) . '/index.html.gz';
479
480 return file_exists($cache_file);
481 }
482
483 function bwp_cache_current_page()
484 {
485 global $bwp_current_page, $berq_log;
486
487 // $berq_log->info('Shutdown hook fired.');
488
489 if (empty($bwp_current_page)) {
490 return;
491 }
492
493 // Check if the current user is logged in or if it's a POST request
494 if (is_user_logged_in() || $_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PURGE') {
495 return;
496 }
497
498 if (is_admin()) {
499 return;
500 }
501
502 if (defined('DOING_CRON') && DOING_CRON) {
503 return;
504 }
505
506 if (defined('DOING_AJAX') && DOING_AJAX) {
507 return;
508 }
509
510 if (defined('REST_REQUEST') && REST_REQUEST) {
511 return;
512 }
513
514 if (php_sapi_name() === 'cli' || defined('WP_CLI')) {
515 return;
516 }
517
518 if (!bwp_is_webpage()) {
519 return;
520 }
521
522 if (!bwp_can_warmup_cache($bwp_current_page)) {
523 return;
524 }
525
526 warmup_cache_by_url($bwp_current_page, false, false);
527 $bwp_current_page = null;
528 }
529
530 function bwp_is_partial_cache($identifier)
531 {
532
533 $cache_directory = bwp_get_cache_dir();
534 $cache_key = md5($identifier);
535 $cache_file = $cache_directory . $cache_key . '.html';
536
537 if (file_exists($cache_file)) {
538 $buffer = file_get_contents($cache_file);
539
540 if (!empty($buffer)) {
541 $html = HtmlDomParser::str_get_html($buffer);
542
543 if ($html !== false) {
544 $style_tag = $html->find('style#berqwp-critical-css', 0);
545
546 if ($style_tag === null) {
547 return true;
548 }
549 }
550 }
551 }
552
553 return false;
554 }
555
556 function berq_is_localhost()
557 {
558 $whitelist = array('127.0.0.1', '::1');
559
560 if (in_array($_SERVER['REMOTE_ADDR'], $whitelist)) {
561 return true;
562 }
563
564 return false;
565 }
566
567 function berqwp_remove_ignore_params($slug)
568 {
569 // List of tracking parameters to remove
570 $tracking_params = get_option('berq_ignore_urls_params', []);
571
572 $tracking_params = apply_filters('berqwp_ignored_urls_params', $tracking_params);
573
574 // Parse the provided slug
575 $url_parts = parse_url($slug);
576
577 // Get the current URL parameters
578 $url_params = array();
579 if (isset($url_parts['query'])) {
580 parse_str($url_parts['query'], $url_params);
581 }
582
583 // Remove specified tracking parameters from the URL
584 foreach ($tracking_params as $param) {
585 $param = trim($param);
586 if (isset($url_params[$param])) {
587 unset($url_params[$param]);
588 }
589 }
590
591 // Build the new query string
592 $new_query_string = http_build_query($url_params);
593
594 // Reconstruct the URL with the new query string
595 $updated_url = $url_parts['scheme'] . "://" . $url_parts['host'] . $url_parts['path'];
596 if (!empty($new_query_string)) {
597 $updated_url .= '?' . $new_query_string;
598 }
599
600 return $updated_url;
601 }
602
603 function berqwp_is_sub_dir_wp()
604 {
605 // // remove http
606 // $site_url = explode('//', home_url())[1];
607 // $break_slash = explode('/', $site_url);
608
609 // return count($break_slash) > 1;
610
611 $site_url = site_url();
612 $home_url = home_url();
613
614 // Parse paths from URLs
615 $site_path = @trim(parse_url($site_url, PHP_URL_PATH), '/');
616 $home_path = @trim(parse_url($home_url, PHP_URL_PATH), '/');
617
618 // Split paths into segments
619 $site_segments = explode('/', $site_path);
620 $home_segments = explode('/', $home_path);
621
622 // Check if Site URL has any segments and is not empty
623 if (!empty($site_path) && count($site_segments) > 0) {
624 // If both paths are the same and contain at least one segment
625 if ($site_path === $home_path && count($site_segments) > 0) {
626 return true; // WordPress is in a subdirectory
627 }
628 }
629
630 // Check if the Home URL has more segments than the Site URL
631 if (count($home_segments) > 1) {
632 return true; // Home URL indicates a subdirectory
633 }
634
635 return false; // Otherwise, it's likely not in a subdirectory
636 }
637
638 function berqwp_current_page_cache_file()
639 {
640 $slug_uri = $_SERVER['REQUEST_URI'];
641
642 // if wordpress is installed in a sub directory
643 if (berqwp_is_sub_dir_wp()) {
644 // Parse strings to extract paths
645 $path1 = explode('/', parse_url(home_url(), PHP_URL_PATH));
646 $path2 = explode('/', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
647
648 // Find the common part of the paths
649 $commonPath = implode('/', array_intersect($path1, $path2));
650
651 // Subtract the common part from the first string
652 $slug_uri = str_replace($commonPath, '', $_SERVER['REQUEST_URI']);
653 }
654
655 // Return if page is excluded from cache
656 $pages_to_exclude = get_option('berq_exclude_urls', []);
657
658 if (in_array(get_site_url() . $slug_uri, $pages_to_exclude)) {
659 return;
660 }
661
662
663 $slug = berqwp_remove_ignore_params($slug_uri);
664
665 if (isset($_GET['creating_cache'])) {
666 return;
667 }
668
669 if (get_option('berqwp_enable_sandbox') == 1 && isset($_GET['berqwp'])) {
670 $slug = explode('?berqwp', $slug_uri)[0];
671 } elseif (get_option('berqwp_enable_sandbox') == 1 && !isset($_GET['creating_cache'])) {
672 return;
673 }
674
675
676 // Attempt to retrieve the cached HTML from the cache directory
677 $cache_directory = bwp_get_cache_dir();
678
679 // Generate a unique cache key based on the current page URL
680 $cache_key = md5($slug);
681 $cache_file = $cache_directory . $cache_key . '.html';
682
683 return $cache_file;
684 }
685
686 function berqwp_get_LCP_details($url, $device = 'mobile')
687 {
688 $google_pagespeed_api_url = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=$url&strategy=$device ";
689
690 // Send a GET request to the Google PageSpeed Insights API
691 // $response = wp_remote_get($google_pagespeed_api_url, array('timeout' => 60));
692 $response = bwp_wp_remote_get($google_pagespeed_api_url, array('timeout' => 60));
693
694 if (is_wp_error($response)) {
695 return 'Error: ' . $response->get_error_message();
696 }
697
698 // Convert the JSON response to a PHP array
699 $body = wp_remote_retrieve_body($response);
700 $output = json_decode($body, true);
701
702 // Get the LCP data
703 return $output['lighthouseResult']['audits']['largest-contentful-paint-element']['details']['items'][0]['items'][0]['node'];
704 }
705
706 function berqwp_enable_advanced_cache($status)
707 {
708 global $berq_log;
709
710 // Get wp-config.php path
711 $configFilePath = defined('BERQWP_WP_CONFIG') ? BERQWP_WP_CONFIG : ABSPATH . 'wp-config.php';
712
713 // Check if the file exists
714 if (!file_exists($configFilePath)) {
715 $berq_log->error("Error: wp-config.php does not exist.");
716 return false;
717 }
718
719 // Check if the file is writable
720 if (!is_writable($configFilePath)) {
721 $berq_log->error("Error: wp-config.php is not writable. Check file permissions.");
722 return false;
723 }
724
725 // Prepare the new WP_CACHE definition
726 $newVal = sprintf("define( 'WP_CACHE', %s ); /* Added by BerqWP */\n", ($status ? "true" : "false"));
727 $replacementVal = sprintf(" %s ", ($status ? "true" : "false"));
728
729 // Read the file into an array of lines
730 $lines = file($configFilePath);
731 if ($lines === false || empty($lines)) {
732 $berq_log->error("Error: Could not read wp-config.php or file is empty.");
733 return false;
734 }
735
736 $wpCacheFound = false;
737 $phpOpeningTagLine = false;
738
739 // Process each line to update an existing WP_CACHE definition
740 foreach ($lines as $lineIndex => &$line) {
741 if (strpos($line, "<?php") !== false && strpos($line, "?>") === false) {
742 $phpOpeningTagLine = $lineIndex;
743 }
744 if (!$wpCacheFound && preg_match("/define\s*\(\s*['\"](.*?)['\"]\s*,(.*?)\)/", $line, $matches)) {
745 if ($matches[1] === "WP_CACHE") {
746 // Replace the existing WP_CACHE value with the new value
747 $line = str_replace($matches[2], $replacementVal, $line);
748 $wpCacheFound = true;
749 }
750 }
751 // If we have found both the opening tag and WP_CACHE, we can stop processing further lines.
752 if ($phpOpeningTagLine !== false && $wpCacheFound !== false) {
753 break;
754 }
755 }
756 unset($line);
757
758 // If WP_CACHE was not found and we are enabling it, insert it after the PHP opening tag.
759 if (!$wpCacheFound && $status) {
760 if ($phpOpeningTagLine !== false) {
761 array_splice($lines, $phpOpeningTagLine + 1, 0, [$newVal]);
762 } else {
763 // If no PHP opening tag is found, prepend a new one with the WP_CACHE definition.
764 array_unshift($lines, "<?php " . trim($newVal) . " ?>\n");
765 }
766 }
767
768 // Rebuild the file content
769 $newContent = implode("", $lines);
770 $tempFile = $configFilePath . '.tmp';
771
772 // Write to a temporary file first (atomic write)
773 if (file_put_contents($tempFile, $newContent, LOCK_EX) === false) {
774 $berq_log->error("Error: Could not write to temporary file: $tempFile");
775 return false;
776 }
777
778 // Rename the temporary file to wp-config.php
779 if (!rename($tempFile, $configFilePath)) {
780 $berq_log->error("Error: Could not rename temporary file to wp-config.php");
781 unlink($tempFile);
782 return false;
783 }
784
785 $berq_log->info("wp-config.php updated successfully.");
786 return true;
787 }
788
789 // Copied from Nginx Helper plugin
790 // function berqwp_unlink_recursive($dir)
791 // {
792
793 // if (!is_dir($dir)) {
794 // return;
795 // }
796
797 // $dh = opendir($dir);
798
799 // if (!$dh) {
800 // return;
801 // }
802
803 // // phpcs:ignore -- WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition -- Variable assignment required for recursion.
804 // while (false !== ($obj = readdir($dh))) {
805
806 // if ('.' === $obj || '..' === $obj) {
807 // continue;
808 // }
809
810 // if (!@unlink($dir . '/' . $obj)) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
811 // berqwp_unlink_recursive($dir . '/' . $obj, false);
812 // }
813 // }
814
815 // closedir($dh);
816 // }
817
818 function berqwp_unlink_recursive($dir)
819 {
820 if (!is_dir($dir)) {
821 return false;
822 }
823
824 $files = new RecursiveIteratorIterator(
825 new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
826 RecursiveIteratorIterator::CHILD_FIRST
827 );
828
829 foreach ($files as $file) {
830 if ($file->isFile()) {
831 if (!unlink($file->getRealPath())) {
832 error_log("Failed to delete file: " . $file->getRealPath());
833 }
834 } elseif ($file->isDir()) {
835 if (!rmdir($file->getRealPath())) {
836 error_log("Failed to delete directory: " . $file->getRealPath());
837 }
838 }
839 }
840
841 return rmdir($dir); // remove the root directory itself
842 }
843
844 function berqwp_get_last_modified_timestamp()
845 {
846 global $post;
847
848 // Check if it's a single post or page
849 if (is_singular()) {
850 return get_the_modified_time('U', $post->ID); // 'U' format parameter returns Unix timestamp
851 }
852
853 // Check if it's a taxonomy term
854 if (is_tax() || is_category() || is_tag()) {
855 $term = get_queried_object(); // Get the current term object
856
857 // For tags, get the last modified date of the most recent post associated with the tag
858 if (is_tag()) {
859 $args = array(
860 'tag_id' => $term->term_id,
861 'posts_per_page' => 1,
862 'orderby' => 'modified',
863 'order' => 'DESC',
864 'fields' => 'ids', // Return only post IDs to reduce overhead
865 );
866 $posts = get_posts($args);
867 if ($posts) {
868 $latest_post_id = $posts[0];
869 return get_the_modified_time('U', $latest_post_id); // 'U' format parameter returns Unix timestamp
870 }
871 }
872
873 // For category archives, get the last modified date of the most recent post within the category
874 if (is_category()) {
875 $args = array(
876 'category' => $term->term_id,
877 'posts_per_page' => 1,
878 'orderby' => 'modified',
879 'order' => 'DESC',
880 'fields' => 'ids', // Return only post IDs to reduce overhead
881 );
882 $posts = get_posts($args);
883 if ($posts) {
884 $latest_post_id = $posts[0];
885 return get_the_modified_time('U', $latest_post_id); // 'U' format parameter returns Unix timestamp
886 }
887 }
888
889 return strtotime($term->modified); // Convert modified date to timestamp
890 }
891
892 // Check if it's an archive
893 if (is_archive()) {
894 // For other archives
895 $archive_id = get_queried_object_id(); // Get the ID of the current archive
896 $archive = get_post($archive_id); // Get the archive post object
897 return strtotime($archive->post_modified); // Convert modified date to timestamp
898 }
899
900 // For other cases (fallback)
901 return false;
902 }
903
904 function bwp_is_gzip_supported()
905 {
906 return function_exists('gzencode') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false;
907 }
908
909 // function bwp_cached_pages_count()
910 // {
911 // $cache_directory = bwp_get_cache_dir();
912 // $cache_files = glob($cache_directory . "*.gz");
913 // return is_array($cache_files) ? count($cache_files) : 0;
914 // }
915
916 function bwp_cached_pages_count()
917 {
918 $cache_directory = bwp_get_cache_dir();
919
920 if (!is_dir($cache_directory)) {
921 return 0;
922 }
923
924 $count = 0;
925
926 $iterator = new RecursiveIteratorIterator(
927 new RecursiveDirectoryIterator($cache_directory, FilesystemIterator::SKIP_DOTS)
928 );
929
930 foreach ($iterator as $file) {
931 if ($file->isFile() && str_ends_with($file->getFilename(), '.gz')) {
932 $count++;
933 }
934 }
935
936 return $count;
937 }
938
939 function bwp_wp_remote_get($url, $args = array())
940 {
941 // Default arguments
942 $defaults = array(
943 'headers' => array(
944 'User-Agent' => 'BerqWP Bot', // Customize user agent if needed
945 ),
946 );
947
948 // Merge provided arguments with defaults
949 $args = wp_parse_args($args, $defaults);
950
951 if (empty($args['timeout'])) {
952 $args['timeout'] = 30;
953 }
954
955 // Initialize cURL session
956 $ch = curl_init();
957
958 // Set the URL
959 curl_setopt($ch, CURLOPT_URL, $url);
960
961 // Set to return the transfer as a string
962 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
963
964 // Set timeout in seconds
965 curl_setopt($ch, CURLOPT_TIMEOUT, $args['timeout']);
966
967 // Set the user-agent
968 curl_setopt($ch, CURLOPT_USERAGENT, $args['headers']['User-Agent']);
969
970 // Include header in the output
971 curl_setopt($ch, CURLOPT_HEADER, true);
972
973 // Execute the request
974 $response = curl_exec($ch);
975
976 // Check for errors
977 if (curl_errno($ch)) {
978 $error_message = curl_error($ch);
979 curl_close($ch);
980 return new WP_Error('curl_error', $error_message);
981 }
982
983 // Close cURL session
984 curl_close($ch);
985
986 // Separate headers and body
987 list($headers, $body) = explode("\r\n\r\n", $response, 2);
988
989 // Parse headers into array
990 $header_lines = explode("\r\n", $headers);
991 $headers = array();
992 foreach ($header_lines as $line) {
993 $parts = explode(':', $line, 2);
994 if (count($parts) == 2) {
995 $headers[trim($parts[0])] = trim($parts[1]);
996 }
997 }
998
999 // Construct response array similar to wp_remote_get
1000 $response = array(
1001 'headers' => $headers,
1002 'body' => $body,
1003 'response' => array(
1004 'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
1005 'message' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
1006 ),
1007 'cookies' => array(),
1008 'filename' => '',
1009 );
1010
1011 return $response;
1012 }
1013
1014 function bwp_is_openlitespeed_server()
1015 {
1016 $is_litespeed = false;
1017 $is_litespeed = isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false;
1018
1019 $headers = getallheaders();
1020 foreach ($headers as $header => $value) {
1021 if (stripos($value, 'LiteSpeed') !== false) {
1022 $is_litespeed = true;
1023 }
1024 }
1025
1026 return $is_litespeed;
1027 }
1028
1029 function verify_request_origin($request)
1030 {
1031 // Check the referrer header to ensure the request is coming from the same site
1032 $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
1033 $site_url = get_site_url();
1034
1035 // Optionally, check the origin header
1036 $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
1037
1038 // Ensure the request comes from the same site
1039 if (strpos($referrer, $site_url) !== 0 && strpos($origin, $site_url) !== 0) {
1040 return new WP_Error('rest_forbidden', esc_html__('You cannot access this resource.', 'searchpro'), array('status' => 403));
1041 }
1042
1043 // Ensure the origin is a subdomain of berqwp.com
1044 if (!preg_match('/^https?:\/\/([a-z0-9-]+\.)?berqwp\.com$/', $origin)) {
1045 return new WP_Error('rest_forbidden', esc_html__('You cannot access this resource.', 'searchpro'), array('status' => 403));
1046 }
1047
1048 return true;
1049 }
1050
1051 function berq_rest_permission_callback(WP_REST_Request $request)
1052 {
1053 // Get the nonce from the request
1054 $nonce = $request->get_header('X-WP-Nonce');
1055 $hash = $request->get_header('X-berqwp-key-hash');
1056
1057 if ($hash == md5(berqwp_get_license_key())) {
1058 return true;
1059 }
1060
1061 // Verify the nonce
1062 if (!wp_verify_nonce($nonce, 'wp_rest')) {
1063 return new WP_Error('rest_invalid_nonce', __('Invalid nonce', 'searchpro'), array('status' => 403));
1064 }
1065
1066 return true; // Return true to allow the request
1067 }
1068
1069 function berq_rest_verify_license_callback(WP_REST_Request $request)
1070 {
1071 $license_key_hash = sanitize_text_field($request->get_param('license_key_hash'));
1072 $license_key = berqwp_get_license_key();
1073
1074 if (empty($license_key_hash) || empty($license_key) || $license_key_hash !== md5($license_key)) {
1075 global $berq_log;
1076 $berq_log->error("Exiting... Invalid license key.");
1077 return new WP_Error('rest_invalid_nonce', __('Invalid license key', 'searchpro'), array('status' => 403));
1078 }
1079
1080 return true; // Return true to allow the request
1081 }
1082
1083 function bwp_dash_notification($msg = '', $status = 'warning')
1084 {
1085 ?>
1086 <div class="berqwp-notification <?php echo esc_attr($status) ?>">
1087 <?php
1088
1089 echo "<div class='icon'>";
1090 if ($status == 'warning') {
1091 echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>';
1092 } elseif ($status == 'error') {
1093 echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c53 0 96 43 96 96l0 3.6c0 15.7-12.7 28.4-28.4 28.4l-135.1 0c-15.7 0-28.4-12.7-28.4-28.4l0-3.6c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4l112 0c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5l64.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-64 0c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6L272 240c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 239.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64.3 0c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"/></svg>';
1094 } elseif ($status == 'info') {
1095 echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M96 64c0-17.7-14.3-32-32-32S32 46.3 32 64l0 256c0 17.7 14.3 32 32 32s32-14.3 32-32L96 64zM64 480a40 40 0 1 0 0-80 40 40 0 1 0 0 80z"/></svg>';
1096 } else {
1097 return;
1098 }
1099 echo "</div>";
1100 echo esc_html($msg);
1101 ?>
1102 </div>
1103 <?php
1104 }
1105
1106 function bwp_can_warmup_cache($identifier)
1107 {
1108
1109 if (get_transient('bwp_warmup_lock_' . md5($identifier)) === false) {
1110
1111 set_transient('bwp_warmup_lock_' . md5($identifier), true, 100);
1112 return true;
1113 }
1114
1115 return false;
1116 }
1117
1118 function bwp_clear_warmup_lock($slug)
1119 {
1120 delete_transient('bwp_warmup_lock_' . md5($slug));
1121 }
1122
1123 function bwp_extractUrlsFromCss($cssContent)
1124 {
1125 $urls = [];
1126 $pattern = '/url\((.*?)\)/i';
1127
1128 preg_match_all($pattern, $cssContent, $matches);
1129
1130 if (!empty($matches[1])) {
1131 foreach ($matches[1] as $match) {
1132 // Trim any surrounding quotes and whitespace
1133 $urls[] = trim($match, '\'" ');
1134 }
1135 }
1136
1137 return $urls;
1138 }
1139
1140 function bwp_getBaseUrl($fileUrl)
1141 {
1142 // Parse the URL and get its components
1143 $parsedUrl = parse_url($fileUrl);
1144 $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] . '://' : '';
1145 $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
1146 $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
1147
1148 // Remove the file name from the path to get the base URL
1149 $basePath = preg_replace('/\/[^\/]+$/', '/', $path);
1150
1151 // Construct the base URL
1152 return $scheme . $host . $basePath;
1153 }
1154
1155 function bwp_rel2abs($rel, $base)
1156 {
1157 /* return if already absolute URL */
1158 if (parse_url($rel, PHP_URL_SCHEME) != '')
1159 return $rel;
1160
1161 /* queries and anchors */
1162 if ($rel[0] == '#' || $rel[0] == '?')
1163 return $base . $rel;
1164
1165 /* parse base URL and convert to local variables:
1166 $scheme, $host, $path */
1167 extract(parse_url($base));
1168
1169 /* remove non-directory element from path */
1170 $path = preg_replace('#/[^/]*$#', '', $path);
1171
1172 /* destroy path if relative url points to root */
1173 if ($rel[0] == '/')
1174 $path = '';
1175
1176 /* dirty absolute URL */
1177 $abs = "$host$path/$rel";
1178
1179 /* replace '//' or '/./' or '/foo/../' with '/' */
1180 $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
1181 for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
1182 }
1183
1184 /* absolute URL is ready! */
1185 return $scheme . '://' . $abs;
1186 }
1187
1188 function update_image_url_extension($image_url, $file_extension)
1189 {
1190 $url_arr = explode('.', $image_url);
1191 $last_index = count($url_arr) - 1;
1192
1193 // Extract the file extension
1194 $current_file_extension = pathinfo($image_url, PATHINFO_EXTENSION);
1195
1196 $url_arr[$last_index] = str_replace("$current_file_extension", $file_extension, $url_arr[$last_index]);
1197 $new_image_url = implode('.', $url_arr);
1198
1199 return $new_image_url;
1200 }
1201
1202 if (!function_exists('str_contains')) {
1203 function str_contains(string $haystack, string $needle): bool
1204 {
1205 return strpos($haystack, $needle) !== false;
1206 }
1207 }
1208
1209 function bwp_check_connection($short_live = false, $force = false)
1210 {
1211
1212 $transient_key = "berqwp_connection_status";
1213
1214 if ($force) {
1215 delete_transient('berqwp_connection_status');
1216 delete_transient('berqwp_connection_status_sl');
1217 }
1218
1219 // Allow cache busting by passing $force_check = true
1220 // if ($force_check) {
1221 // delete_transient($transient_key);
1222 // }
1223
1224 if ($short_live) {
1225 $transient_key = "berqwp_connection_status_sl";
1226 }
1227
1228 // Check if the result is cached
1229 $cached_status = get_transient($transient_key);
1230
1231 if ($cached_status) {
1232 return $cached_status;
1233 }
1234
1235 // Perform the actual REST API check
1236 $response = wp_safe_remote_get('https://boost.berqwp.com/photon/?connection_test=1&url=' . bwp_admin_home_url('/?utm_source=' . time()), ['timeout' => 60]);
1237
1238 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1239 return ['status' => 'success']; // Skip if server is unreachable
1240 // $result = array(
1241 // 'status' => 'error',
1242 // 'message' => 'The site is not accessible. Error: ' . $response->get_error_message(),
1243 // );
1244 } else {
1245 $body = wp_remote_retrieve_body($response);
1246
1247 if ($body == 'pingpong') {
1248 $result = array(
1249 'status' => 'success',
1250 'message' => 'Your website is accessible by BerqWP server.',
1251 );
1252 } else {
1253 $result = array(
1254 'status' => 'error',
1255 'message' => 'BerqWP server is unable to access your website, please whitelist our server IP address.',
1256 );
1257 }
1258 }
1259
1260 if ($short_live) {
1261 set_transient($transient_key, $result, 60 * 5);
1262 } else {
1263 set_transient($transient_key, $result, 60 * 60 * 24);
1264 }
1265
1266 return $result;
1267 }
1268
1269 function bwp_sendPostRequestInBackground($url, $params)
1270 {
1271 $userAgent = 'BerqWP';
1272 $urlParts = parse_url($url);
1273
1274 // Ensure we have a valid path and handle query strings
1275 $path = isset($urlParts['path']) ? $urlParts['path'] : '/';
1276 if (isset($urlParts['query'])) {
1277 $path .= '?' . $urlParts['query'];
1278 }
1279
1280 $postString = http_build_query($params);
1281
1282 $host = $urlParts['host'];
1283 $scheme = isset($urlParts['scheme']) ? $urlParts['scheme'] : 'http';
1284 $port = ($scheme === 'https') ? 443 : 80;
1285 $transport = ($scheme === 'https') ? 'ssl://' : '';
1286
1287 $request = "POST $path HTTP/1.1\r\n";
1288 $request .= "Host: $host\r\n";
1289 $request .= "User-Agent: $userAgent\r\n";
1290 $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
1291 $request .= "Content-Length: " . strlen($postString) . "\r\n";
1292 $request .= "Connection: Close\r\n\r\n";
1293 $request .= $postString;
1294
1295 $fp = fsockopen($transport . $host, $port, $errno, $errstr, 30);
1296
1297 if ($fp) {
1298 fwrite($fp, $request);
1299 fclose($fp); // Close immediately to prevent waiting for the response
1300 } else {
1301 // If fsockopen fails, fallback to wp_remote_post
1302 $args = array(
1303 'timeout' => 0.01,
1304 'headers' => array(
1305 'User-Agent' => $userAgent,
1306 ),
1307 'body' => $params
1308 );
1309
1310 wp_remote_post($url, $args);
1311 }
1312 }
1313
1314 function bwp_is_webpage()
1315 {
1316 $headers = headers_list();
1317 $contentType = '';
1318
1319 foreach ($headers as $header) {
1320 if (stripos($header, 'Content-Type') !== false) {
1321 $contentType = $header;
1322 break;
1323 }
1324 }
1325
1326 if (stripos($contentType, 'text/html') !== false) {
1327 return true;
1328 }
1329
1330 return false;
1331 }
1332
1333 function bwp_isGzipEncoded()
1334 {
1335 // Check if the Content-Encoding header is set to gzip
1336 if (isset($_SERVER['HTTP_CONTENT_ENCODING']) && strtolower($_SERVER['HTTP_CONTENT_ENCODING']) === 'gzip') {
1337 return true;
1338 }
1339
1340 foreach (headers_list() as $header) {
1341 if (stripos($header, 'Content-Encoding: gzip') !== false) {
1342 return true;
1343 }
1344 }
1345
1346 return false;
1347 }
1348
1349 function bwp_sluguri_into_path($slug_uri)
1350 {
1351 $is_multisite = function_exists('is_multisite') && is_multisite();
1352 $home_path = parse_url(berqwp_home_url(), PHP_URL_PATH);
1353
1354 // if (berqwp_is_sub_dir_wp() && !$is_multisite) {
1355 if (!$is_multisite && $home_path !== null) {
1356 // Parse strings to extract paths
1357 $path1 = explode('/', $home_path);
1358 $path2 = explode('/', parse_url($slug_uri, PHP_URL_PATH));
1359
1360 // Find the common part of the paths
1361 $commonPath = implode('/', array_intersect($path1, $path2));
1362
1363 // Subtract the common part from the first string
1364 $slug_uri = str_replace($commonPath, '', $slug_uri);
1365 }
1366
1367 return $slug_uri;
1368 }
1369
1370 function bwp_url_into_path($url)
1371 {
1372 $parsed_url = parse_url($url);
1373 $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
1374 $query = isset($parsed_url['query']) ? $parsed_url['query'] : '';
1375 $homeurl = berqwp_home_url();
1376
1377 if (!empty($query)) {
1378 $path = $path . '?' . $query;
1379 }
1380
1381 $is_multisite = function_exists('is_multisite') && is_multisite();
1382 $home_path = parse_url($homeurl, PHP_URL_PATH);
1383
1384 if (!$is_multisite && $home_path !== null) {
1385 // Parse strings to extract paths
1386 $path1 = explode('/', $home_path);
1387 $path2 = explode('/', parse_url($path, PHP_URL_PATH));
1388
1389 // Find the common part of the paths
1390 $commonPath = implode('/', array_intersect($path1, $path2));
1391
1392 // Subtract the common part from the first string
1393 $path = str_replace($commonPath, '', $path);
1394 }
1395
1396 // $path = bwp_intersect_str(bwp_admin_home_url(), $path);
1397
1398 return $path;
1399 }
1400
1401 function bwp_get_cache_dir()
1402 {
1403 $cache_directory = optifer_cache . 'html/';
1404
1405 if (function_exists('is_multisite') && is_multisite()) {
1406 $site_id = get_current_blog_id();
1407 $cache_directory .= 'site-' . $site_id . '/';
1408 }
1409
1410 if (!is_dir($cache_directory)) {
1411 wp_mkdir_p($cache_directory);
1412 }
1413
1414 return $cache_directory;
1415 }
1416
1417 function bwp_get_static_cache_dir()
1418 {
1419 $dir = optifer_cache . 'static/';
1420
1421 if (function_exists('is_multisite') && is_multisite()) {
1422 $dir .= 'site-' . get_current_blog_id() . '/';
1423 }
1424
1425 if (!is_dir($dir)) {
1426 wp_mkdir_p($dir);
1427 }
1428
1429 return $dir;
1430 }
1431
1432 function bwp_webhook()
1433 {
1434 require_once optifer_PATH . '/api/webhook.php';
1435 }
1436
1437 function bwp_update_configs_webhook()
1438 {
1439 require_once optifer_PATH . '/api/update_configs.php';
1440 }
1441
1442 function bwp_check_status_webhook()
1443 {
1444 require_once optifer_PATH . '/api/check_status.php';
1445 }
1446
1447 function bwp_handle_request_cache()
1448 {
1449 require_once optifer_PATH . '/api/request_cache.php';
1450 }
1451
1452 function bwp_get_translatepress_urls($page_url)
1453 {
1454 $trp = TRP_Translate_Press::get_trp_instance();
1455 $trp_settings = get_option('trp_settings', array());
1456 $languages = $trp->get_component('languages');
1457 $url_converter = $trp->get_component('url_converter');
1458 $publish_languages = $languages->get_language_names($trp_settings['publish-languages'], 'english_name');
1459 $urls = [];
1460
1461
1462 if (!empty($publish_languages)) {
1463 foreach ($publish_languages as $key => $value) {
1464 $translation_url = $url_converter->get_url_for_language($key, $page_url, '');
1465 $urls[] = $translation_url;
1466 }
1467 }
1468
1469 return $urls;
1470 }
1471
1472 function bwp_can_optimize_page_url($page_url)
1473 {
1474 $slug = bwp_url_into_path($page_url);
1475 if (berqwp_is_slug_excludable($slug)) {
1476 return false;
1477 }
1478
1479 if (berqwp_is_page_url_excluded($page_url)) {
1480 return false;
1481 }
1482
1483 return true;
1484 }
1485
1486 function bwp_get_sitemap()
1487 {
1488 if (isset($_GET['berqwp_sitemap'])) {
1489
1490
1491 // Get post types to optimize
1492 $post_types = get_option('berqwp_optimize_post_types');
1493
1494 $post_types = apply_filters('berqwp_warmup_post_types', $post_types);
1495 $rewrite_map = apply_filters('berqwp_warmup_post_types_rewrite_map', []);
1496
1497 // Set the number of posts per batch to handle
1498 $posts_per_page = 10000; // Adjust this based on server capacity
1499
1500 // Get current page from query string (for pagination)
1501 $paged = isset($_GET['paged']) ? intval($_GET['paged']) : 1;
1502
1503 // Build the query arguments with pagination
1504 $args = array(
1505 'post_type' => $post_types,
1506 'post_status' => array('publish'), // Only published posts
1507 'posts_per_page' => $posts_per_page,
1508 'paged' => $paged,
1509 'fields' => 'ids', // Only retrieve IDs to save memory
1510 );
1511
1512 // Run the query
1513 $query = new WP_Query($args);
1514 $total_posts = $query->found_posts; // Get the total number of posts
1515
1516 // Calculate total pages
1517 $total_pages = ceil($total_posts / $posts_per_page);
1518
1519 // Output JSON header
1520 header('Content-Type: application/json');
1521
1522 $post_params = berqwp_get_page_params('/');
1523 unset($post_params['license_key']);
1524 unset($post_params['page_slug']);
1525 unset($post_params['page_url']);
1526 $post_params['key'] = '';
1527
1528 $berqconfigs = berqConfigs::getInstance();
1529 $configs = $berqconfigs->get_configs();
1530 $post_params['cache_lifespan'] = $configs['cache_lifespan'];
1531 $post_params['sandbox'] = get_option('berqwp_enable_sandbox');
1532
1533 if ($query->have_posts()) {
1534 $sitemap_urls = [];
1535
1536 if (!isset($_GET['configs_only'])) {
1537
1538 if (get_option('show_on_front') !== 'page') {
1539 $sitemap_urls[] = home_url('/');
1540 }
1541
1542 // Loop through the posts and generate URLs
1543 while ($query->have_posts()) {
1544 $query->the_post();
1545
1546 $post_id = get_the_ID();
1547 $post_type = get_post_field( 'post_type', $post_id );
1548 $url = get_permalink( $post_id );
1549
1550 // Fallback for MB CPTs whose rewrite rules aren't loaded (post type not registered)
1551 if ( strpos( $url, '?' ) !== false && isset( $rewrite_map[ $post_type ] ) ) {
1552 $post_name = get_post_field( 'post_name', $post_id );
1553 $url = trailingslashit( home_url( '/' . $rewrite_map[ $post_type ] . '/' . $post_name ) );
1554 }
1555
1556 // SEO Generator
1557 if (class_exists('NSG_Seo_Generator')) {
1558 $nsg = NSG_Seo_Generator::get_instance();
1559 $slugs = $nsg->nsg_get_seo_page_slugs( $post_id );
1560
1561 if ( is_array( $slugs ) ) {
1562 foreach ( $slugs as $slug ) {
1563 $generated_url = trailingslashit( $url ) . $slug . '/';
1564
1565 if (bwp_can_optimize_page_url($generated_url)) {
1566 $sitemap_urls[] = $generated_url;
1567 }
1568 }
1569 }
1570 }
1571
1572 $translation_urls = apply_filters('berqwp_page_translation_urls', [], $url);
1573
1574 if (!bwp_can_optimize_page_url($url)) {
1575 continue;
1576 }
1577
1578 if (!in_array($url, $sitemap_urls)) {
1579 $sitemap_urls[] = $url;
1580 }
1581
1582 if (!empty($translation_urls)) {
1583 foreach ($translation_urls as $page_url) {
1584 if (!bwp_can_optimize_page_url($page_url)) {
1585 continue;
1586 }
1587
1588 if (!in_array($page_url, $sitemap_urls)) {
1589 $sitemap_urls[] = $page_url;
1590 }
1591 }
1592 }
1593 }
1594 }
1595
1596 // Return response with pagination info
1597 $response = array(
1598 'paged' => $paged,
1599 'post_params' => $post_params,
1600 'total_pages' => $total_pages,
1601 'total_posts' => $total_posts,
1602 'urls' => $sitemap_urls,
1603 );
1604
1605 echo json_encode($response);
1606 } else {
1607 // If no posts found
1608 echo json_encode(array('error' => 'No posts found'));
1609 }
1610
1611 // Clean up
1612 wp_reset_postdata();
1613 exit;
1614 }
1615 }
1616
1617 function bwp_show_account()
1618 {
1619 if (!defined('BERQWP_HIDE_ACCOUNT')) {
1620 return true;
1621 }
1622
1623 if (defined('BERQWP_HIDE_ACCOUNT') && BERQWP_HIDE_ACCOUNT) {
1624 return false;
1625 }
1626
1627 return true;
1628 }
1629
1630 function bwp_show_docs()
1631 {
1632
1633 if (!defined('BERQWP_HIDE_DOCS')) {
1634 return true;
1635 }
1636
1637 if (defined('BERQWP_HIDE_DOCS') && BERQWP_HIDE_DOCS) {
1638 return false;
1639 }
1640
1641 return true;
1642 }
1643
1644 function bwp_admin_home_url($relative_path = '')
1645 {
1646 $home_url = home_url();
1647
1648 if (class_exists('TRP_Translate_Press')) {
1649 $trp = TRP_Translate_Press::get_trp_instance();
1650 $trp_settings = get_option('trp_settings', array());
1651 $default_lang = $trp_settings['default-language'];
1652 $url_converter = $trp->get_component('url_converter');
1653 $url_slugs = $trp_settings['url-slugs'];
1654
1655 if (!empty($default_lang) && $trp_settings['add-subdirectory-to-default-language'] == 'yes' && !empty($url_slugs[$default_lang]) && strpos($home_url, "/$url_slugs[$default_lang]") === false) {
1656 // var_dump($trp_settings);
1657 return $home_url . '/' . $url_slugs[$default_lang] . $relative_path;
1658 }
1659 }
1660
1661 return $home_url . $relative_path;
1662 }
1663
1664 function bwp_intersect_str($str1, $str2)
1665 {
1666 $arr = explode('/', $str1);
1667 $last_item = $arr[count($arr) - 1]; // Get last item from $arr
1668 $arr2 = explode('/', $str2);
1669
1670 if (!empty($arr2) && strpos($str2, $last_item) !== false) {
1671
1672 for ($i = 1; $i < count($arr2); $i++) {
1673 if (!empty($arr2[$i]) && $arr2[$i] == $last_item) {
1674 unset($arr2[$i]);
1675 }
1676 }
1677
1678 $str2 = implode('/', $arr2);
1679 }
1680
1681 return $str2;
1682 }
1683
1684 function bwp_is_tab($tab_id)
1685 {
1686 $default_tab = 'dashboard';
1687
1688 if (!empty($_GET['tab_id'])) {
1689 if (sanitize_text_field($_GET['tab_id']) == $tab_id) {
1690 // echo ' style="display:block" ';
1691 echo ' style="visibility:visible;opacity:1;height:auto;display:block;" ';
1692 } else {
1693 // echo ' style="display:none" ';
1694 echo ' style="visibility:hidden;opacity:0;height:0;overflow:hidden;display:none;" ';
1695 }
1696 } else {
1697
1698 if ($tab_id == $default_tab) {
1699 // echo ' style="display:block" ';
1700 echo ' style="visibility:visible;opacity:1;height:auto;display:block;" ';
1701 } else {
1702 // echo ' style="display:none" ';
1703 echo ' style="visibility:hidden;opacity:0;height:0;overflow:hidden;display:none;" ';
1704 }
1705 }
1706 }
1707
1708 function bwp_is_tab_nav($tab_id)
1709 {
1710 $default_tab = 'dashboard';
1711
1712 if (!empty($_GET['tab_id'])) {
1713 if (sanitize_text_field($_GET['tab_id']) == $tab_id) {
1714 echo ' active ';
1715 }
1716 } else {
1717
1718 if ($tab_id == $default_tab) {
1719 echo ' active ';
1720 }
1721 }
1722 }
1723
1724 function bwp_notice($status = '', $title = null, $message = null, $btn = [], $dismissible = false)
1725 {
1726 ?>
1727 <div
1728 class="bwp-notice <?php echo 'status-' . esc_attr($status); ?> <?php echo $dismissible ? 'dismissible' : ''; ?> <?php echo empty($title) ? 'no-title' : ''; ?>">
1729 <?php if ($dismissible) { ?>
1730 <div class="close"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"
1731 fill="#000">
1732 <path
1733 d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
1734 </svg></div>
1735 <?php } ?>
1736
1737 <div class="icon"><svg width="30" height="30" viewBox="0 0 24 24" fill="#fff" xmlns="http://www.w3.org/2000/svg">
1738 <path fill-rule="evenodd" clip-rule="evenodd"
1739 d="M6.43896 0H17.561C21.1172 0 24 2.88287 24 6.43903V17.561C24 21.1171 21.1172 24 17.561 24H6.43896C2.88281 24 0 21.1171 0 17.561V6.43903C0 2.88287 2.88281 0 6.43896 0ZM15.7888 4.09753L8.59961 12.7534H12.3517L7.02441 20.4878L16.3903 11.0222L12.7814 10.3799L15.7888 4.09753Z"
1740 fill="#1f72ff" />
1741 </svg></div>
1742 <div class="content">
1743 <div class="status-tag"><?php echo esc_html($status); ?></div>
1744 <?php if (!empty($title)) { ?>
1745 <h5><?php echo wp_kses_post($title); ?></h5>
1746 <?php } ?>
1747
1748 <?php if (!empty($message)) { ?>
1749 <?php echo wp_kses_post($message); ?>
1750 <?php } ?>
1751
1752 <?php if (!empty($btn) && is_array($btn)) { ?>
1753 <div class="bwp-notice-btn">
1754 <?php
1755 foreach ($btn as $bwp_btn) {
1756 if (empty($bwp_btn['target'])) {
1757 $bwp_btn['target'] = '';
1758 }
1759
1760 if (empty($bwp_btn['href'])) {
1761 $bwp_btn['href'] = '';
1762 }
1763
1764 if (empty($bwp_btn['classes'])) {
1765 $bwp_btn['classes'] = '';
1766 }
1767
1768 if (empty($bwp_btn['text'])) {
1769 $bwp_btn['text'] = '';
1770 }
1771 ?>
1772 <a target="<?php echo esc_attr($bwp_btn['target']) ?? ''; ?>"
1773 href="<?php echo esc_attr($bwp_btn['href']) ?? ''; ?>"
1774 class="bwp-btn <?php echo esc_attr($bwp_btn['classes']) ?? ''; ?>">
1775 <?php echo esc_html($bwp_btn['text']); ?>
1776 </a>
1777 <?php
1778 }
1779 ?>
1780 </div>
1781 <?php } ?>
1782 </div>
1783 </div>
1784 <?php
1785 }
1786
1787 function bwp_is_option_updated($option_name)
1788 {
1789 $value = isset($_POST[$option_name]) ? $_POST[$option_name] : 0;
1790
1791 if ($value == 'on') {
1792 $value = 1;
1793 }
1794
1795 $option_val = get_option($option_name);
1796 $array_options = [
1797 'berq_exclude_js_css',
1798 'berq_exclude_cdn',
1799 'berqwp_force_include_critical_css',
1800 'berqwp_exclude_lazy_load_images',
1801 'berqwp_exclude_third_party_js',
1802 'berqwp_exclude_js',
1803 'berqwp_exclude_css',
1804 ];
1805
1806 if (in_array($option_name, $array_options)) {
1807 $urls = sanitize_textarea_field($value);
1808 $urls_array = explode("\n", $urls);
1809
1810 $urls_array = array_map(function ($kw) {
1811 return trim($kw);
1812 }, $urls_array);
1813
1814 $urls_array = array_filter($urls_array, function ($kw) {
1815 return !empty($kw);
1816 });
1817
1818 if (!is_array($option_val)) {
1819 $option_val = [];
1820 }
1821
1822 // var_dump($option_val, $urls_array, array_diff($option_val, $urls_array), array_diff($option_val, $urls_array));
1823 // exit;
1824
1825 return !empty(array_diff($option_val, $urls_array));
1826 }
1827
1828 $values_changed = $option_val == $value;
1829
1830 return !$values_changed;
1831 }
1832
1833 function bwp_request_purge_license_key_cache()
1834 {
1835
1836 $license_key = berqwp_get_license_key();
1837 $parsed_url = parse_url(home_url());
1838 $domain = $parsed_url['host'];
1839
1840 if (empty($domain) || empty($license_key)) {
1841 return;
1842 }
1843
1844 wp_remote_post(BerqWP::$endpoint."license/flush", [
1845 'timeout' => 30,
1846 'body' => [
1847 'domain' => $domain,
1848 'license_key' => $license_key,
1849 'site_url' => home_url(),
1850 ]
1851 ]);
1852
1853 }
1854
1855 function bwp_display_logs()
1856 {
1857
1858 if (isset($_GET['berqwp_logs'])) {
1859
1860 // Check if the current user is logged in and has admin privileges
1861 if (!is_user_logged_in() || !current_user_can('administrator')) {
1862 wp_die(__('You are not allowed to access this page.', 'searchpro'));
1863 }
1864
1865 // Define the path to the BerqWP logs file
1866 $log_file_path = optifer_cache . '/logs/berqwp.log'; // Adjust path if needed
1867
1868 // Check if the log file exists
1869 if (!file_exists($log_file_path)) {
1870 echo '<div>No logs available. The log file does not exist.</div>';
1871 exit;
1872 }
1873
1874 // Read and display the log file content
1875 $logs = file_get_contents($log_file_path);
1876
1877 if ($logs === false) {
1878 echo '<div>Unable to read the log file.</div>';
1879 exit;
1880 }
1881
1882 echo '<h2>BerqWP Logs</h2>';
1883 echo '<pre style="background:#f4f4f4;padding:15px;border:1px solid #ddd;">' . esc_html($logs) . '</pre>';
1884 exit;
1885 }
1886 }
1887
1888 function bwp_cf_flush_all()
1889 {
1890 if (!empty(get_option('berqwp_cf_creden'))) {
1891 $email = get_option('berqwp_cf_creden')['email'];
1892 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
1893 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
1894
1895 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
1896 $berqCloudflareAPIHandler->purge_all_cache();
1897 }
1898 }
1899
1900 function bwp_cf_flush_page($url)
1901 {
1902 if (!empty(get_option('berqwp_cf_creden'))) {
1903 $email = get_option('berqwp_cf_creden')['email'];
1904 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
1905 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
1906
1907 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
1908 $berqCloudflareAPIHandler->flush_url($url);
1909 }
1910 }
1911
1912 function bwp_cf_delete_rules()
1913 {
1914 if (!empty(get_option('berqwp_cf_creden'))) {
1915 $email = get_option('berqwp_cf_creden')['email'];
1916 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
1917 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
1918
1919 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
1920 $berqCloudflareAPIHandler->delete_rule_by_description('BerqWP cache rules');
1921 }
1922 }
1923
1924 function bwp_lock_cache_directory()
1925 {
1926 $cache_dir = optifer_cache;
1927
1928 if (!file_exists($cache_dir)) {
1929 wp_mkdir_p($cache_dir);
1930 }
1931
1932 if (!function_exists('got_mod_rewrite')) {
1933 require_once ABSPATH . 'wp-admin/includes/misc.php';
1934 }
1935
1936 if (!got_mod_rewrite()) {
1937 return;
1938 }
1939
1940 $rules = <<<HTACCESS
1941 Order allow,deny
1942 Deny from all
1943
1944 <FilesMatch "index\.html\.gz$">
1945 Order allow,deny
1946 Allow from all
1947 </FilesMatch>
1948
1949 # Block PHP execution and dot-files to prevent webshell deployment
1950 <FilesMatch "\.php\d*$|\.phtml$|\.phar$">
1951 Order allow,deny
1952 Deny from all
1953 </FilesMatch>
1954 <FilesMatch "^\.">
1955 Order allow,deny
1956 Deny from all
1957 </FilesMatch>
1958 HTACCESS;
1959
1960 file_put_contents($cache_dir . '.htaccess', $rules);
1961
1962 // Suppress directory listing
1963 $index_stub = $cache_dir . 'index.php';
1964 if (!file_exists($index_stub)) {
1965 file_put_contents($index_stub, '<?php // Silence is golden');
1966 }
1967
1968 }
1969
1970 function bwp_delete_php_files_in_cache()
1971 {
1972 $cache_dir = optifer_cache;
1973 if (!is_dir($cache_dir)) {
1974 return;
1975 }
1976
1977 $php_extensions = ['php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phtml', 'phar'];
1978
1979 $iterator = new RecursiveIteratorIterator(
1980 new RecursiveDirectoryIterator($cache_dir, RecursiveDirectoryIterator::SKIP_DOTS)
1981 );
1982
1983 foreach ($iterator as $file) {
1984 if ($file->isFile() && in_array(strtolower($file->getExtension()), $php_extensions, true)) {
1985 @unlink($file->getRealPath());
1986 }
1987 }
1988 }
1989
1990 function bwp_write_htaccess_rules($ignore_sandbox = false)
1991 {
1992 if (!got_mod_rewrite() || bwp_is_openlitespeed_server()) {
1993 return;
1994 }
1995
1996 if (!$ignore_sandbox && get_option('berqwp_enable_sandbox') == 1) {
1997 return;
1998 }
1999
2000 $htaccess = get_home_path() . '.htaccess';
2001 $cache_tag_host = parse_url(home_url(), PHP_URL_HOST);
2002
2003 // Match the cache-serving RewriteCond to the site's actual permalink convention,
2004 // so caching works whether the site uses trailing slashes or not.
2005 $permalink_structure = get_option('permalink_structure');
2006 $uses_trailing_slash = empty($permalink_structure) || substr(rtrim($permalink_structure), -1) === '/';
2007
2008 $slash_conds = $uses_trailing_slash
2009 ? [' RewriteCond %{REQUEST_URI} /$']
2010 : [' RewriteCond %{REQUEST_URI} ^/$ [OR]', ' RewriteCond %{REQUEST_URI} !/$'];
2011
2012 $rules = array_merge([
2013 '<IfModule mod_rewrite.c>',
2014 ' RewriteEngine On',
2015 ' RewriteCond %{REQUEST_METHOD} !POST',
2016 ' RewriteCond %{QUERY_STRING} ^$',
2017 ' RewriteCond %{HTTP_COOKIE} !(wp\-postpass|wordpress_logged_in|comment_author|woocommerce_cart_hash|edd_items_in_cart) [NC]',
2018 ' RewriteCond %{HTTP_USER_AGENT} !(Googlebot|Google-InspectionTool|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|Sogou|Exabot|facebookexternalhit|Twitterbot|LinkedInBot|WhatsApp|TelegramBot|Applebot|AhrefsBot|SemrushBot|MJ12bot|DotBot|PetalBot|BLEXBot|archive\.org_bot|UptimeRobot|Pingdom|ChatGPT-User|GPTBot|ClaudeBot|Bytespider) [NC]',
2019 ], $slash_conds, [
2020 ' RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/berqwp/html/%{HTTP_HOST}%{REQUEST_URI}/index.html.gz -f',
2021 ' RewriteRule .* /wp-content/cache/berqwp/html/%{HTTP_HOST}%{REQUEST_URI}/index.html.gz [L]',
2022 '</IfModule>',
2023 '<IfModule mod_mime.c>',
2024 ' <FilesMatch "index\.html\.gz$">',
2025 ' ForceType text/html',
2026 ' AddEncoding gzip .gz',
2027 ' AddDefaultCharset utf-8',
2028 ' </FilesMatch>',
2029 '</IfModule>',
2030 '<IfModule mod_headers.c>',
2031 ' <FilesMatch "index\.html\.gz$">',
2032 ' Header set X-BerqWP-Cache "HIT"',
2033 ' Header set X-Served "server"',
2034 ' Header set Content-Encoding "gzip"',
2035 ' Header set Vary "Accept-Encoding, Cookie"',
2036 ' Header set CDN-Cache-Control "max-age=2592000"',
2037 ' Header set Cache-Control "public, max-age=600, s-maxage=2592000, stale-while-revalidate=86400"',
2038 ' Header set Cache-Tag "' . $cache_tag_host . '"',
2039 ' Header set X-Content-Type-Options "nosniff"',
2040 ' </FilesMatch>',
2041 '</IfModule>',
2042 '<IfModule mod_expires.c>',
2043 ' ExpiresActive On',
2044 ' ExpiresByType text/css "access plus 1 year"',
2045 ' ExpiresByType application/javascript "access plus 1 year"',
2046 ' ExpiresByType text/javascript "access plus 1 year"',
2047 ' ExpiresByType image/jpeg "access plus 1 year"',
2048 ' ExpiresByType image/png "access plus 1 year"',
2049 ' ExpiresByType image/gif "access plus 1 year"',
2050 ' ExpiresByType image/webp "access plus 1 year"',
2051 ' ExpiresByType image/avif "access plus 1 year"',
2052 ' ExpiresByType image/svg+xml "access plus 1 year"',
2053 ' ExpiresByType image/x-icon "access plus 1 year"',
2054 ' ExpiresByType font/woff "access plus 1 year"',
2055 ' ExpiresByType font/woff2 "access plus 1 year"',
2056 ' ExpiresByType application/font-woff "access plus 1 year"',
2057 ' ExpiresByType application/font-woff2 "access plus 1 year"',
2058 ' ExpiresByType application/x-font-ttf "access plus 1 year"',
2059 ' ExpiresByType application/x-font-opentype "access plus 1 year"',
2060 '</IfModule>',
2061 '<IfModule mod_headers.c>',
2062 ' <FilesMatch "\.(css|js)$">',
2063 ' Header set Cache-Control "public, max-age=31536000, immutable"',
2064 ' </FilesMatch>',
2065 ' <FilesMatch "\.(jpg|jpeg|png|gif|webp|avif|svg|ico)$">',
2066 ' Header set Cache-Control "public, max-age=31536000, immutable"',
2067 ' </FilesMatch>',
2068 ' <FilesMatch "\.(woff|woff2|ttf|otf|eot)$">',
2069 ' Header set Cache-Control "public, max-age=31536000, immutable"',
2070 ' Header set Access-Control-Allow-Origin "*"',
2071 ' </FilesMatch>',
2072 '</IfModule>',
2073 ]);
2074
2075 // Must appear before the WordPress block so Apache processes it first
2076 $marker = 'BerqWP Cache';
2077 $start = "# BEGIN $marker";
2078 $end = "# END $marker";
2079 $wp_start = '# BEGIN WordPress';
2080
2081 $block = $start . "\n";
2082 $block .= implode("\n", $rules) . "\n";
2083 $block .= $end . "\n";
2084
2085 $contents = file_exists($htaccess) ? file_get_contents($htaccess) : '';
2086
2087 // Remove all existing BerqWP Cache blocks (handles duplicates and corrupt state)
2088 $contents = preg_replace(
2089 '/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms',
2090 '',
2091 $contents
2092 );
2093
2094 // Insert before first WordPress block if present, otherwise prepend
2095 if (strpos($contents, $wp_start) !== false) {
2096 $contents = preg_replace('/' . preg_quote($wp_start, '/') . '/', $block . "\n" . $wp_start, $contents, 1);
2097 } else {
2098 $contents = $block . "\n" . $contents;
2099 }
2100
2101 file_put_contents($htaccess, $contents);
2102 }
2103
2104 function bwp_remove_htaccess_rules()
2105 {
2106 $htaccess = get_home_path() . '.htaccess';
2107
2108 if (!file_exists($htaccess)) {
2109 return;
2110 }
2111
2112 $contents = file_get_contents($htaccess);
2113 $contents = preg_replace('/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms', '', $contents);
2114 file_put_contents($htaccess, $contents);
2115 }
2116
2117 function bwp_sync_htaccess_on_sandbox_change()
2118 {
2119 // Action fires before update_option, so check the incoming POST value
2120 $sandbox_enabling = isset($_POST['berqwp_enable_sandbox']);
2121
2122 if ($sandbox_enabling) {
2123 bwp_remove_htaccess_rules();
2124 } else {
2125 bwp_write_htaccess_rules(true);
2126 }
2127 }
2128
2129 function berqwp_is_wp_cron_broken()
2130 {
2131 $cron = _get_cron_array();
2132 if (!$cron || !is_array($cron))
2133 return false;
2134
2135 $now = time();
2136 foreach ($cron as $timestamp => $events) {
2137 if ($timestamp < $now - 300) { // 5 minutes overdue
2138 return true;
2139 }
2140 }
2141 return false;
2142 }
2143
2144 function berqwp_is_page_url_excluded($page_url)
2145 {
2146 if (empty($page_url)) {
2147 return false;
2148 }
2149
2150 $pages_to_exclude = get_option('berq_exclude_urls', []);
2151
2152 if (strpos($page_url, '?') !== false) {
2153 $page_url = explode('?', $page_url)[0];
2154 }
2155
2156 if (in_array($page_url, $pages_to_exclude)) {
2157 return true;
2158 }
2159
2160 if (!empty($pages_to_exclude)) {
2161 foreach ($pages_to_exclude as $single_page_url) {
2162
2163 if (empty($single_page_url)) {
2164 continue;
2165 }
2166
2167 if (substr($single_page_url, -1) !== '*') {
2168 continue;
2169 }
2170
2171 $single_page_url = str_replace('*', '', $single_page_url);
2172
2173 if (strpos($page_url, $single_page_url) !== false) {
2174 return true;
2175 }
2176 }
2177 }
2178
2179 return false;
2180 }
2181
2182 // function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2183 // {
2184 // // Load the $buffer content into Simple HTML DOM
2185 // $html = HtmlDomParser::str_get_html($buffer);
2186
2187 // if (!$html) {
2188 // return $buffer;
2189 // }
2190
2191 // // Find the <body> tag
2192 // $body = $html->find('body', 0);
2193
2194 // // If the body tag exists, append the new HTML
2195 // if ($body) {
2196 // // Use htmlspecialchars to properly encode the inserted content
2197 // // Avoid breaking the HTML by not using innerhtml directly
2198 // $body->innertext .= $htmlToAppend;
2199 // } else {
2200 // // If no body tag exists, append the HTML to the document directly
2201 // $html->innertext .= $htmlToAppend;
2202 // }
2203
2204 // // Return the modified HTML content
2205 // return $html->save();
2206 // }
2207 //
2208 function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2209 {
2210 $pos = strripos($buffer, '</body>');
2211 if ($pos !== false) {
2212 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2213 }
2214
2215 // Fallback: try before </html>
2216 $pos = strripos($buffer, '</html>');
2217 if ($pos !== false) {
2218 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2219 }
2220
2221 // Last resort: append to end
2222 return $buffer . $htmlToAppend;
2223 }
2224
2225 function berqwp_appendHtmlToHead($buffer, $htmlToAppend)
2226 {
2227 $pos = stripos($buffer, '</head>');
2228 if ($pos !== false) {
2229 $buffer = substr_replace($buffer, PHP_EOL . $htmlToAppend, $pos, 0);
2230 }
2231
2232 // Fix missing </body> tag
2233 if (stripos($buffer, '</body>') === false) {
2234 $buffer = str_replace('</html>', '</body>' . PHP_EOL . '</html>', $buffer);
2235 }
2236
2237 return $buffer;
2238 }
2239
2240 function berqwp_prependHtmlToHead($buffer, $htmlToPrepend)
2241 {
2242 if (preg_match('/<head(\s[^>]*)?>/i', $buffer, $matches, PREG_OFFSET_CAPTURE)) {
2243 $insertPos = $matches[0][1] + strlen($matches[0][0]);
2244 return substr_replace($buffer, PHP_EOL . $htmlToPrepend, $insertPos, 0);
2245 }
2246
2247 // Fallback: no <head> tag found, return unchanged
2248 return $buffer;
2249 }
2250
2251 function berqwp_earlyHeadHtml($buffer, $htmlToInsert)
2252 {
2253 // Locate <head> opening tag
2254 if (!preg_match('/<head(\s[^>]*)?>/i', $buffer, $headMatch, PREG_OFFSET_CAPTURE)) {
2255 return berqwp_prependHtmlToHead($buffer, $htmlToInsert);
2256 }
2257
2258 $headStart = $headMatch[0][1] + strlen($headMatch[0][0]);
2259
2260 // Bound the search to content inside <head>...</head>
2261 $headEndPos = stripos($buffer, '</head>', $headStart);
2262 if ($headEndPos === false) {
2263 return berqwp_prependHtmlToHead($buffer, $htmlToInsert);
2264 }
2265
2266 $headContent = substr($buffer, $headStart, $headEndPos - $headStart);
2267
2268 // Find first <script, <link, or <style in the head
2269 // if (preg_match('/<(script|link|style)[\s>\/]/i', $headContent, $tagMatch, PREG_OFFSET_CAPTURE)) {
2270
2271 $regex = "/<(script|link[^>]+rel=['\"]+(modulepreload|stylesheet|preload)['\"]|style)[\s>\/]/i";
2272
2273 if (preg_match($regex, $headContent, $tagMatch, PREG_OFFSET_CAPTURE)) {
2274 $insertPos = $headStart + $tagMatch[0][1];
2275 return substr_replace($buffer, $htmlToInsert . PHP_EOL, $insertPos, 0);
2276 }
2277
2278 // Fallback: no matching tag in head
2279 return berqwp_prependHtmlToHead($buffer, $htmlToInsert);
2280 }
2281
2282
2283 function berqwp_setup_dropin()
2284 {
2285 if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
2286 $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
2287 } else {
2288 $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
2289 }
2290
2291 // Remove advanced-cache.php when sandbox mode is enabled
2292 if (get_option('berqwp_enable_sandbox') == 1) {
2293
2294 if (file_exists($adv_cache_path)) {
2295 @unlink($adv_cache_path);
2296 }
2297
2298 return;
2299 }
2300
2301 $bwp_adv_cache = optifer_PATH . 'advanced-cache.php';
2302
2303 if (!file_exists($adv_cache_path) || (file_exists($adv_cache_path) && is_writable($adv_cache_path))) {
2304 if (
2305 (!file_exists($adv_cache_path) && get_option('berqwp_enable_sandbox') !== 1)
2306 || (file_exists($adv_cache_path) && md5_file($adv_cache_path) !== md5_file($bwp_adv_cache))
2307 ) {
2308
2309 // Dynamically create the drop-in file
2310 $dropin_content = file_get_contents(optifer_PATH . 'advanced-cache.php');
2311
2312 // Write the drop-in content to the file, replacing any existing file
2313 file_put_contents($adv_cache_path, $dropin_content);
2314
2315 // Enable wp cache in wp-config.php
2316 berqwp_enable_advanced_cache(true);
2317 }
2318 }
2319
2320
2321 // if (get_option('berqwp_enable_sandbox') == 1 && file_exists($adv_cache_path)) {
2322 // unlink($adv_cache_path);
2323 // }
2324 }
2325
2326 function berqwp_is_file_url($url)
2327 {
2328 $parsed = parse_url($url);
2329 if (!isset($parsed['path']))
2330 return false;
2331
2332 $extension = pathinfo($parsed['path'], PATHINFO_EXTENSION);
2333
2334 // allow html pages
2335 if (strtolower($extension) === 'html' || strtolower($extension) === 'htm') {
2336 return false;
2337 }
2338
2339 // Returns true if there is a file extension, false otherwise
2340 return !empty($extension);
2341 }
2342
2343 function berqwp_clear_cache_queue()
2344 {
2345 $berqconfigs = berqConfigs::getInstance();
2346 $site_id = $berqconfigs->get_configs()['site_id'];
2347 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
2348 $berqwp->clear_cache_queue(home_url(), $site_id);
2349 }
2350
2351 function berqwp_sync_addons($license_key, $site_url)
2352 {
2353 if (empty($license_key) || empty($site_url)) {
2354 return false;
2355 }
2356
2357 $post_data = [
2358 'license_key' => $license_key,
2359 'site_url' => $site_url,
2360 ];
2361
2362 $args = array(
2363 'body' => wp_json_encode($post_data),
2364 'method' => 'POST',
2365 'timeout' => 20,
2366 'headers' => array(
2367 'Content-Type' => 'application/json',
2368 ),
2369 );
2370
2371 global $berq_log;
2372 $berq_log->info("Trigger addon sync");
2373
2374 $response = wp_remote_post('https://berqwp.com/wp-json/berqwp/active-addons', $args);
2375 $response_body = wp_remote_retrieve_body($response);
2376 $json = json_decode($response_body, true);
2377
2378 if (empty($json) || $json['status'] !== 'success') {
2379 return false;
2380 }
2381
2382 if (is_array($json['active_addons'])) {
2383
2384 $enable_fluid_images = in_array('fluid_images', $json['active_addons']);
2385 if ($enable_fluid_images && !get_option('berqwp_can_use_fluid_images')) {
2386 update_option('berqwp_can_use_fluid_images', 1);
2387 update_option('bwp_require_flush_cache', 1);
2388 } elseif (!$enable_fluid_images) {
2389 update_option('berqwp_can_use_fluid_images', 0);
2390 }
2391 }
2392 }
2393
2394 function berqwp_validate_url_array($urls)
2395 {
2396 $home_host = parse_url(home_url(), PHP_URL_HOST);
2397
2398 $urls = array_filter($urls, function ($url) use ($home_host) {
2399 $url_host = parse_url($url, PHP_URL_HOST);
2400
2401 // Relative URLs → treat as same host
2402 if (empty($url_host)) {
2403 return true;
2404 }
2405
2406 return $url_host === $home_host;
2407 });
2408
2409 return array_values($urls);
2410 }
2411
2412 function berqwp_render_toggle($name, $checked) {
2413 ?>
2414 <label class="berq-toggle">
2415 <input type="checkbox" name="<?php echo esc_attr($name); ?>" <?php checked(1, $checked, true); ?>>
2416 <div class="berq-toggle-switch"></div>
2417 </label>
2418 <?php
2419 }
2420
2421 function bwp_log_recently_optimized_page($page_url)
2422 {
2423 if (empty($page_url)) return;
2424
2425 // berqwp_stored_page_cache passes a slug/path, not a full URL — reconstruct it
2426 if (!str_starts_with($page_url, 'http')) {
2427 $page_url = home_url($page_url);
2428 }
2429 $page_url = urldecode($page_url);
2430
2431 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2432 if (!is_array($log)) $log = [];
2433
2434 // Remove existing entry for this URL so re-caching moves it to the top
2435 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2436
2437 array_unshift($log, [
2438 'url' => esc_url_raw($page_url),
2439 'cached_at' => current_time('Y-m-d H:i:s'),
2440 ]);
2441
2442 if (count($log) > 200) {
2443 $log = array_slice($log, 0, 200);
2444 }
2445
2446 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2447 }
2448
2449 function bwp_clear_recently_optimized_log()
2450 {
2451 delete_option('berqwp_recently_optimized');
2452 }
2453
2454 function bwp_remove_recently_optimized_entry($page_url)
2455 {
2456 if (empty($page_url)) return;
2457
2458 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2459 if (!is_array($log)) return;
2460
2461 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2462 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2463 }
2464
2465 function berqwp_can_use_cloud() {
2466 $berqconfigs = berqConfigs::getInstance();
2467 $configs = $berqconfigs->get_configs();
2468 $license_key = berqwp_get_license_key();
2469
2470 return !empty($configs['site_id']) && !empty($configs['secret']) && !empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && !empty($license_key);
2471 }
2472
2473 function berqwp_generate_site_id() {
2474 $blog_id = get_current_blog_id();
2475 $network_id = function_exists('get_current_network_id') ? get_current_network_id() : 1;
2476 $siteurl = get_option('siteurl');
2477 return md5("berqwp|$network_id|$blog_id|$siteurl");
2478 }
2479