PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / trunk
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript vtrunk
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 1.9.7 All 169 releases
searchpro / inc / helper-functions.php

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

2,354 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 ' AddDefaultCharset utf-8',
1942 ' </FilesMatch>',
1943 '</IfModule>',
1944 '<IfModule mod_headers.c>',
1945 ' <FilesMatch "index\.html\.gz$">',
1946 ' Header set X-BerqWP-Cache "HIT"',
1947 ' Header set X-Served "server"',
1948 ' Header set Content-Encoding "gzip"',
1949 ' Header set Vary "Accept-Encoding, Cookie"',
1950 ' Header set CDN-Cache-Control "max-age=2592000"',
1951 ' Header set Cache-Control "public, max-age=600, s-maxage=2592000, stale-while-revalidate=86400"',
1952 ' Header set Cache-Tag "' . $cache_tag_host . '"',
1953 ' Header set X-Content-Type-Options "nosniff"',
1954 ' </FilesMatch>',
1955 '</IfModule>',
1956 '<IfModule mod_expires.c>',
1957 ' ExpiresActive On',
1958 ' ExpiresByType text/css "access plus 1 year"',
1959 ' ExpiresByType application/javascript "access plus 1 year"',
1960 ' ExpiresByType text/javascript "access plus 1 year"',
1961 ' ExpiresByType image/jpeg "access plus 1 year"',
1962 ' ExpiresByType image/png "access plus 1 year"',
1963 ' ExpiresByType image/gif "access plus 1 year"',
1964 ' ExpiresByType image/webp "access plus 1 year"',
1965 ' ExpiresByType image/avif "access plus 1 year"',
1966 ' ExpiresByType image/svg+xml "access plus 1 year"',
1967 ' ExpiresByType image/x-icon "access plus 1 year"',
1968 ' ExpiresByType font/woff "access plus 1 year"',
1969 ' ExpiresByType font/woff2 "access plus 1 year"',
1970 ' ExpiresByType application/font-woff "access plus 1 year"',
1971 ' ExpiresByType application/font-woff2 "access plus 1 year"',
1972 ' ExpiresByType application/x-font-ttf "access plus 1 year"',
1973 ' ExpiresByType application/x-font-opentype "access plus 1 year"',
1974 '</IfModule>',
1975 '<IfModule mod_headers.c>',
1976 ' <FilesMatch "\.(css|js)$">',
1977 ' Header set Cache-Control "public, max-age=31536000, immutable"',
1978 ' </FilesMatch>',
1979 ' <FilesMatch "\.(jpg|jpeg|png|gif|webp|avif|svg|ico)$">',
1980 ' Header set Cache-Control "public, max-age=31536000, immutable"',
1981 ' </FilesMatch>',
1982 ' <FilesMatch "\.(woff|woff2|ttf|otf|eot)$">',
1983 ' Header set Cache-Control "public, max-age=31536000, immutable"',
1984 ' Header set Access-Control-Allow-Origin "*"',
1985 ' </FilesMatch>',
1986 '</IfModule>',
1987 ];
1988
1989 // Must appear before the WordPress block so Apache processes it first
1990 $marker = 'BerqWP Cache';
1991 $start = "# BEGIN $marker";
1992 $end = "# END $marker";
1993 $wp_start = '# BEGIN WordPress';
1994
1995 $block = $start . "\n";
1996 $block .= implode("\n", $rules) . "\n";
1997 $block .= $end . "\n";
1998
1999 $contents = file_exists($htaccess) ? file_get_contents($htaccess) : '';
2000
2001 // Remove all existing BerqWP Cache blocks (handles duplicates and corrupt state)
2002 $contents = preg_replace(
2003 '/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms',
2004 '',
2005 $contents
2006 );
2007
2008 // Insert before first WordPress block if present, otherwise prepend
2009 if (strpos($contents, $wp_start) !== false) {
2010 $contents = preg_replace('/' . preg_quote($wp_start, '/') . '/', $block . "\n" . $wp_start, $contents, 1);
2011 } else {
2012 $contents = $block . "\n" . $contents;
2013 }
2014
2015 file_put_contents($htaccess, $contents);
2016 }
2017
2018 function bwp_remove_htaccess_rules()
2019 {
2020 $htaccess = get_home_path() . '.htaccess';
2021
2022 if (!file_exists($htaccess)) {
2023 return;
2024 }
2025
2026 $contents = file_get_contents($htaccess);
2027 $contents = preg_replace('/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms', '', $contents);
2028 file_put_contents($htaccess, $contents);
2029 }
2030
2031 function bwp_sync_htaccess_on_sandbox_change()
2032 {
2033 // Action fires before update_option, so check the incoming POST value
2034 $sandbox_enabling = isset($_POST['berqwp_enable_sandbox']);
2035
2036 if ($sandbox_enabling) {
2037 bwp_remove_htaccess_rules();
2038 } else {
2039 bwp_write_htaccess_rules(true);
2040 }
2041 }
2042
2043 function berqwp_is_wp_cron_broken()
2044 {
2045 $cron = _get_cron_array();
2046 if (!$cron || !is_array($cron))
2047 return false;
2048
2049 $now = time();
2050 foreach ($cron as $timestamp => $events) {
2051 if ($timestamp < $now - 300) { // 5 minutes overdue
2052 return true;
2053 }
2054 }
2055 return false;
2056 }
2057
2058 function berqwp_is_page_url_excluded($page_url)
2059 {
2060 if (empty($page_url)) {
2061 return false;
2062 }
2063
2064 $pages_to_exclude = get_option('berq_exclude_urls', []);
2065
2066 if (strpos($page_url, '?') !== false) {
2067 $page_url = explode('?', $page_url)[0];
2068 }
2069
2070 if (in_array($page_url, $pages_to_exclude)) {
2071 return true;
2072 }
2073
2074 if (!empty($pages_to_exclude)) {
2075 foreach ($pages_to_exclude as $single_page_url) {
2076
2077 if (empty($single_page_url)) {
2078 continue;
2079 }
2080
2081 if (substr($single_page_url, -1) !== '*') {
2082 continue;
2083 }
2084
2085 $single_page_url = str_replace('*', '', $single_page_url);
2086
2087 if (strpos($page_url, $single_page_url) !== false) {
2088 return true;
2089 }
2090 }
2091 }
2092
2093 return false;
2094 }
2095
2096 // function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2097 // {
2098 // // Load the $buffer content into Simple HTML DOM
2099 // $html = HtmlDomParser::str_get_html($buffer);
2100
2101 // if (!$html) {
2102 // return $buffer;
2103 // }
2104
2105 // // Find the <body> tag
2106 // $body = $html->find('body', 0);
2107
2108 // // If the body tag exists, append the new HTML
2109 // if ($body) {
2110 // // Use htmlspecialchars to properly encode the inserted content
2111 // // Avoid breaking the HTML by not using innerhtml directly
2112 // $body->innertext .= $htmlToAppend;
2113 // } else {
2114 // // If no body tag exists, append the HTML to the document directly
2115 // $html->innertext .= $htmlToAppend;
2116 // }
2117
2118 // // Return the modified HTML content
2119 // return $html->save();
2120 // }
2121 //
2122 function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2123 {
2124 $pos = strripos($buffer, '</body>');
2125 if ($pos !== false) {
2126 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2127 }
2128
2129 // Fallback: try before </html>
2130 $pos = strripos($buffer, '</html>');
2131 if ($pos !== false) {
2132 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2133 }
2134
2135 // Last resort: append to end
2136 return $buffer . $htmlToAppend;
2137 }
2138
2139 function berqwp_appendHtmlToHead($buffer, $htmlToAppend)
2140 {
2141 $pos = stripos($buffer, '</head>');
2142 if ($pos !== false) {
2143 $buffer = substr_replace($buffer, PHP_EOL . $htmlToAppend, $pos, 0);
2144 }
2145
2146 // Fix missing </body> tag
2147 if (stripos($buffer, '</body>') === false) {
2148 $buffer = str_replace('</html>', '</body>' . PHP_EOL . '</html>', $buffer);
2149 }
2150
2151 return $buffer;
2152 }
2153
2154 function berqwp_prependHtmlToHead($buffer, $htmlToPrepend)
2155 {
2156 if (preg_match('/<head(\s[^>]*)?>/i', $buffer, $matches, PREG_OFFSET_CAPTURE)) {
2157 $insertPos = $matches[0][1] + strlen($matches[0][0]);
2158 return substr_replace($buffer, PHP_EOL . $htmlToPrepend, $insertPos, 0);
2159 }
2160
2161 // Fallback: no <head> tag found, return unchanged
2162 return $buffer;
2163 }
2164
2165 function berqwp_setup_dropin()
2166 {
2167 if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
2168 $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
2169 } else {
2170 $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
2171 }
2172
2173 // Remove advanced-cache.php when sandbox mode is enabled
2174 if (get_option('berqwp_enable_sandbox') == 1) {
2175
2176 if (file_exists($adv_cache_path)) {
2177 @unlink($adv_cache_path);
2178 }
2179
2180 return;
2181 }
2182
2183 $bwp_adv_cache = optifer_PATH . 'advanced-cache.php';
2184
2185 if (!file_exists($adv_cache_path) || (file_exists($adv_cache_path) && is_writable($adv_cache_path))) {
2186 if (
2187 (!file_exists($adv_cache_path) && get_option('berqwp_enable_sandbox') !== 1)
2188 || (file_exists($adv_cache_path) && md5_file($adv_cache_path) !== md5_file($bwp_adv_cache))
2189 ) {
2190
2191 // Dynamically create the drop-in file
2192 $dropin_content = file_get_contents(optifer_PATH . 'advanced-cache.php');
2193
2194 // Write the drop-in content to the file, replacing any existing file
2195 file_put_contents($adv_cache_path, $dropin_content);
2196
2197 // Enable wp cache in wp-config.php
2198 berqwp_enable_advanced_cache(true);
2199 }
2200 }
2201
2202
2203 // if (get_option('berqwp_enable_sandbox') == 1 && file_exists($adv_cache_path)) {
2204 // unlink($adv_cache_path);
2205 // }
2206 }
2207
2208 function berqwp_is_file_url($url)
2209 {
2210 $parsed = parse_url($url);
2211 if (!isset($parsed['path']))
2212 return false;
2213
2214 $extension = pathinfo($parsed['path'], PATHINFO_EXTENSION);
2215
2216 // allow html pages
2217 if (strtolower($extension) === 'html' || strtolower($extension) === 'htm') {
2218 return false;
2219 }
2220
2221 // Returns true if there is a file extension, false otherwise
2222 return !empty($extension);
2223 }
2224
2225 function berqwp_clear_cache_queue()
2226 {
2227 $berqconfigs = berqConfigs::getInstance();
2228 $site_id = $berqconfigs->get_configs()['site_id'];
2229 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
2230 $berqwp->clear_cache_queue(home_url(), $site_id);
2231 }
2232
2233 function berqwp_sync_addons($license_key, $site_url)
2234 {
2235 if (empty($license_key) || empty($site_url)) {
2236 return false;
2237 }
2238
2239 $post_data = [
2240 'license_key' => $license_key,
2241 'site_url' => $site_url,
2242 ];
2243
2244 $args = array(
2245 'body' => wp_json_encode($post_data),
2246 'method' => 'POST',
2247 'timeout' => 20,
2248 'headers' => array(
2249 'Content-Type' => 'application/json',
2250 ),
2251 );
2252
2253 global $berq_log;
2254 $berq_log->info("Trigger addon sync");
2255
2256 $response = wp_remote_post('https://berqwp.com/wp-json/berqwp/active-addons', $args);
2257 $response_body = wp_remote_retrieve_body($response);
2258 $json = json_decode($response_body, true);
2259
2260 if (empty($json) || $json['status'] !== 'success') {
2261 return false;
2262 }
2263
2264 if (is_array($json['active_addons'])) {
2265
2266 $enable_fluid_images = in_array('fluid_images', $json['active_addons']);
2267 if ($enable_fluid_images && !get_option('berqwp_can_use_fluid_images')) {
2268 update_option('berqwp_can_use_fluid_images', 1);
2269 update_option('bwp_require_flush_cache', 1);
2270 } elseif (!$enable_fluid_images) {
2271 update_option('berqwp_can_use_fluid_images', 0);
2272 }
2273 }
2274 }
2275
2276 function berqwp_validate_url_array($urls)
2277 {
2278 $home_host = parse_url(home_url(), PHP_URL_HOST);
2279
2280 $urls = array_filter($urls, function ($url) use ($home_host) {
2281 $url_host = parse_url($url, PHP_URL_HOST);
2282
2283 // Relative URLs → treat as same host
2284 if (empty($url_host)) {
2285 return true;
2286 }
2287
2288 return $url_host === $home_host;
2289 });
2290
2291 return array_values($urls);
2292 }
2293
2294 function berqwp_render_toggle($name, $checked) {
2295 ?>
2296 <label class="berq-toggle">
2297 <input type="checkbox" name="<?php echo esc_attr($name); ?>" <?php checked(1, $checked, true); ?>>
2298 <div class="berq-toggle-switch"></div>
2299 </label>
2300 <?php
2301 }
2302
2303 function bwp_log_recently_optimized_page($page_url)
2304 {
2305 if (empty($page_url)) return;
2306
2307 // berqwp_stored_page_cache passes a slug/path, not a full URL — reconstruct it
2308 if (!str_starts_with($page_url, 'http')) {
2309 $page_url = home_url($page_url);
2310 }
2311 $page_url = urldecode($page_url);
2312
2313 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2314 if (!is_array($log)) $log = [];
2315
2316 // Remove existing entry for this URL so re-caching moves it to the top
2317 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2318
2319 array_unshift($log, [
2320 'url' => esc_url_raw($page_url),
2321 'cached_at' => current_time('Y-m-d H:i:s'),
2322 ]);
2323
2324 if (count($log) > 200) {
2325 $log = array_slice($log, 0, 200);
2326 }
2327
2328 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2329 }
2330
2331 function bwp_clear_recently_optimized_log()
2332 {
2333 delete_option('berqwp_recently_optimized');
2334 }
2335
2336 function bwp_remove_recently_optimized_entry($page_url)
2337 {
2338 if (empty($page_url)) return;
2339
2340 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2341 if (!is_array($log)) return;
2342
2343 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2344 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2345 }
2346
2347 function berqwp_can_use_cloud() {
2348 $berqconfigs = berqConfigs::getInstance();
2349 $configs = $berqconfigs->get_configs();
2350 $license_key = berqwp_get_license_key();
2351
2352 return !empty($configs['site_id']) && !empty($configs['secret']) && !empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && !empty($license_key);
2353 }
2354