PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.14
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.14
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 1.9.4 1.9.5 1.9.6 All 170 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.14, at inc/helper-functions.php

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