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

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