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

2,321 lines 69.1 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
1484 if ($query->have_posts()) {
1485 $sitemap_urls = [];
1486
1487 if (!isset($_GET['configs_only'])) {
1488
1489 if (get_option('show_on_front') !== 'page') {
1490 $sitemap_urls[] = home_url('/');
1491 }
1492
1493 // Loop through the posts and generate URLs
1494 while ($query->have_posts()) {
1495 $query->the_post();
1496
1497 $post_id = get_the_ID();
1498 $post_type = get_post_field( 'post_type', $post_id );
1499 $url = get_permalink( $post_id );
1500
1501 // Fallback for MB CPTs whose rewrite rules aren't loaded (post type not registered)
1502 if ( strpos( $url, '?' ) !== false && isset( $rewrite_map[ $post_type ] ) ) {
1503 $post_name = get_post_field( 'post_name', $post_id );
1504 $url = trailingslashit( home_url( '/' . $rewrite_map[ $post_type ] . '/' . $post_name ) );
1505 }
1506
1507 // SEO Generator
1508 if (class_exists('NSG_Seo_Generator')) {
1509 $nsg = NSG_Seo_Generator::get_instance();
1510 $slugs = $nsg->nsg_get_seo_page_slugs( $post_id );
1511
1512 if ( is_array( $slugs ) ) {
1513 foreach ( $slugs as $slug ) {
1514 $generated_url = trailingslashit( $url ) . $slug . '/';
1515
1516 if (bwp_can_optimize_page_url($generated_url)) {
1517 $sitemap_urls[] = $generated_url;
1518 }
1519 }
1520 }
1521 }
1522
1523 $translation_urls = apply_filters('berqwp_page_translation_urls', [], $url);
1524
1525 if (!bwp_can_optimize_page_url($url)) {
1526 continue;
1527 }
1528
1529 if (!in_array($url, $sitemap_urls)) {
1530 $sitemap_urls[] = $url;
1531 }
1532
1533 if (!empty($translation_urls)) {
1534 foreach ($translation_urls as $page_url) {
1535 if (!bwp_can_optimize_page_url($page_url)) {
1536 continue;
1537 }
1538
1539 if (!in_array($page_url, $sitemap_urls)) {
1540 $sitemap_urls[] = $page_url;
1541 }
1542 }
1543 }
1544 }
1545 }
1546
1547 // Return response with pagination info
1548 $response = array(
1549 'paged' => $paged,
1550 'post_params' => $post_params,
1551 'total_pages' => $total_pages,
1552 'total_posts' => $total_posts,
1553 'urls' => $sitemap_urls,
1554 );
1555
1556 echo json_encode($response);
1557 } else {
1558 // If no posts found
1559 echo json_encode(array('error' => 'No posts found'));
1560 }
1561
1562 // Clean up
1563 wp_reset_postdata();
1564 exit;
1565 }
1566 }
1567
1568 function bwp_show_account()
1569 {
1570 if (!defined('BERQWP_HIDE_ACCOUNT')) {
1571 return true;
1572 }
1573
1574 if (defined('BERQWP_HIDE_ACCOUNT') && BERQWP_HIDE_ACCOUNT) {
1575 return false;
1576 }
1577
1578 return true;
1579 }
1580
1581 function bwp_show_docs()
1582 {
1583
1584 if (!defined('BERQWP_HIDE_DOCS')) {
1585 return true;
1586 }
1587
1588 if (defined('BERQWP_HIDE_DOCS') && BERQWP_HIDE_DOCS) {
1589 return false;
1590 }
1591
1592 return true;
1593 }
1594
1595 function bwp_admin_home_url($relative_path = '')
1596 {
1597 $home_url = home_url();
1598
1599 if (class_exists('TRP_Translate_Press')) {
1600 $trp = TRP_Translate_Press::get_trp_instance();
1601 $trp_settings = get_option('trp_settings', array());
1602 $default_lang = $trp_settings['default-language'];
1603 $url_converter = $trp->get_component('url_converter');
1604 $url_slugs = $trp_settings['url-slugs'];
1605
1606 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) {
1607 // var_dump($trp_settings);
1608 return $home_url . '/' . $url_slugs[$default_lang] . $relative_path;
1609 }
1610 }
1611
1612 return $home_url . $relative_path;
1613 }
1614
1615 function bwp_intersect_str($str1, $str2)
1616 {
1617 $arr = explode('/', $str1);
1618 $last_item = $arr[count($arr) - 1]; // Get last item from $arr
1619 $arr2 = explode('/', $str2);
1620
1621 if (!empty($arr2) && strpos($str2, $last_item) !== false) {
1622
1623 for ($i = 1; $i < count($arr2); $i++) {
1624 if (!empty($arr2[$i]) && $arr2[$i] == $last_item) {
1625 unset($arr2[$i]);
1626 }
1627 }
1628
1629 $str2 = implode('/', $arr2);
1630 }
1631
1632 return $str2;
1633 }
1634
1635 function bwp_is_tab($tab_id)
1636 {
1637 $default_tab = 'dashboard';
1638
1639 if (!empty($_GET['tab_id'])) {
1640 if (sanitize_text_field($_GET['tab_id']) == $tab_id) {
1641 // echo ' style="display:block" ';
1642 echo ' style="visibility:visible;opacity:1;height:auto;" ';
1643 } else {
1644 // echo ' style="display:none" ';
1645 echo ' style="visibility:hidden;opacity:0;height:0;overflow:hidden;" ';
1646 }
1647 } else {
1648
1649 if ($tab_id == $default_tab) {
1650 // echo ' style="display:block" ';
1651 echo ' style="visibility:visible;opacity:1;height:auto;" ';
1652 } else {
1653 // echo ' style="display:none" ';
1654 echo ' style="visibility:hidden;opacity:0;height:0;overflow:hidden;" ';
1655 }
1656 }
1657 }
1658
1659 function bwp_is_tab_nav($tab_id)
1660 {
1661 $default_tab = 'dashboard';
1662
1663 if (!empty($_GET['tab_id'])) {
1664 if (sanitize_text_field($_GET['tab_id']) == $tab_id) {
1665 echo ' active ';
1666 }
1667 } else {
1668
1669 if ($tab_id == $default_tab) {
1670 echo ' active ';
1671 }
1672 }
1673 }
1674
1675 function bwp_notice($status = '', $title = null, $message = null, $btn = [], $dismissible = false)
1676 {
1677 ?>
1678 <div
1679 class="bwp-notice <?php echo 'status-' . esc_attr($status); ?> <?php echo $dismissible ? 'dismissible' : ''; ?> <?php echo empty($title) ? 'no-title' : ''; ?>">
1680 <?php if ($dismissible) { ?>
1681 <div class="close"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"
1682 fill="#000">
1683 <path
1684 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" />
1685 </svg></div>
1686 <?php } ?>
1687
1688 <div class="icon"><svg width="30" height="30" viewBox="0 0 24 24" fill="#fff" xmlns="http://www.w3.org/2000/svg">
1689 <path fill-rule="evenodd" clip-rule="evenodd"
1690 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"
1691 fill="#1f72ff" />
1692 </svg></div>
1693 <div class="content">
1694 <div class="status-tag"><?php echo esc_html($status); ?></div>
1695 <?php if (!empty($title)) { ?>
1696 <h5><?php echo wp_kses_post($title); ?></h5>
1697 <?php } ?>
1698
1699 <?php if (!empty($message)) { ?>
1700 <?php echo wp_kses_post($message); ?>
1701 <?php } ?>
1702
1703 <?php if (!empty($btn) && is_array($btn)) { ?>
1704 <div class="bwp-notice-btn">
1705 <?php
1706 foreach ($btn as $bwp_btn) {
1707 if (empty($bwp_btn['target'])) {
1708 $bwp_btn['target'] = '';
1709 }
1710
1711 if (empty($bwp_btn['href'])) {
1712 $bwp_btn['href'] = '';
1713 }
1714
1715 if (empty($bwp_btn['classes'])) {
1716 $bwp_btn['classes'] = '';
1717 }
1718
1719 if (empty($bwp_btn['text'])) {
1720 $bwp_btn['text'] = '';
1721 }
1722 ?>
1723 <a target="<?php echo esc_attr($bwp_btn['target']) ?? ''; ?>"
1724 href="<?php echo esc_attr($bwp_btn['href']) ?? ''; ?>"
1725 class="bwp-btn <?php echo esc_attr($bwp_btn['classes']) ?? ''; ?>">
1726 <?php echo esc_html($bwp_btn['text']); ?>
1727 </a>
1728 <?php
1729 }
1730 ?>
1731 </div>
1732 <?php } ?>
1733 </div>
1734 </div>
1735 <?php
1736 }
1737
1738 function bwp_is_option_updated($option_name)
1739 {
1740 $value = isset($_POST[$option_name]) ? $_POST[$option_name] : 0;
1741
1742 if ($value == 'on') {
1743 $value = 1;
1744 }
1745
1746 $option_val = get_option($option_name);
1747 $array_options = [
1748 'berq_exclude_js_css',
1749 'berq_exclude_cdn',
1750 'berqwp_force_include_critical_css',
1751 'berqwp_exclude_lazy_load_images',
1752 'berqwp_exclude_third_party_js',
1753 'berqwp_exclude_js',
1754 'berqwp_exclude_css',
1755 ];
1756
1757 if (in_array($option_name, $array_options)) {
1758 $urls = sanitize_textarea_field($value);
1759 $urls_array = explode("\n", $urls);
1760
1761 $urls_array = array_map(function ($kw) {
1762 return trim($kw);
1763 }, $urls_array);
1764
1765 $urls_array = array_filter($urls_array, function ($kw) {
1766 return !empty($kw);
1767 });
1768
1769 if (!is_array($option_val)) {
1770 $option_val = [];
1771 }
1772
1773 // var_dump($option_val, $urls_array, array_diff($option_val, $urls_array), array_diff($option_val, $urls_array));
1774 // exit;
1775
1776 return !empty(array_diff($option_val, $urls_array));
1777 }
1778
1779 $values_changed = $option_val == $value;
1780
1781 return !$values_changed;
1782 }
1783
1784 function bwp_request_purge_license_key_cache()
1785 {
1786
1787 $license_key = berqwp_get_license_key();
1788 $parsed_url = parse_url(home_url());
1789 $domain = $parsed_url['host'];
1790
1791 if (empty($domain) || empty($license_key)) {
1792 return;
1793 }
1794
1795 wp_remote_post(BerqWP::$endpoint."license/flush", [
1796 'timeout' => 30,
1797 'body' => [
1798 'domain' => $domain,
1799 'license_key' => $license_key,
1800 'site_url' => home_url(),
1801 ]
1802 ]);
1803
1804 }
1805
1806 function bwp_display_logs()
1807 {
1808
1809 if (isset($_GET['berqwp_logs'])) {
1810
1811 // Check if the current user is logged in and has admin privileges
1812 if (!is_user_logged_in() || !current_user_can('administrator')) {
1813 wp_die(__('You are not allowed to access this page.', 'searchpro'));
1814 }
1815
1816 // Define the path to the BerqWP logs file
1817 $log_file_path = optifer_cache . '/logs/berqwp.log'; // Adjust path if needed
1818
1819 // Check if the log file exists
1820 if (!file_exists($log_file_path)) {
1821 echo '<div>No logs available. The log file does not exist.</div>';
1822 exit;
1823 }
1824
1825 // Read and display the log file content
1826 $logs = file_get_contents($log_file_path);
1827
1828 if ($logs === false) {
1829 echo '<div>Unable to read the log file.</div>';
1830 exit;
1831 }
1832
1833 echo '<h2>BerqWP Logs</h2>';
1834 echo '<pre style="background:#f4f4f4;padding:15px;border:1px solid #ddd;">' . esc_html($logs) . '</pre>';
1835 exit;
1836 }
1837 }
1838
1839 function bwp_cf_flush_all()
1840 {
1841 if (!empty(get_option('berqwp_cf_creden'))) {
1842 $email = get_option('berqwp_cf_creden')['email'];
1843 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
1844 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
1845
1846 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
1847 $berqCloudflareAPIHandler->purge_all_cache();
1848 }
1849 }
1850
1851 function bwp_cf_flush_page($url)
1852 {
1853 if (!empty(get_option('berqwp_cf_creden'))) {
1854 $email = get_option('berqwp_cf_creden')['email'];
1855 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
1856 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
1857
1858 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
1859 $berqCloudflareAPIHandler->flush_url($url);
1860 }
1861 }
1862
1863 function bwp_cf_delete_rules()
1864 {
1865 if (!empty(get_option('berqwp_cf_creden'))) {
1866 $email = get_option('berqwp_cf_creden')['email'];
1867 $apitoken = get_option('berqwp_cf_creden')['apitoken'];
1868 $zoneid = get_option('berqwp_cf_creden')['zoneid'];
1869
1870 $berqCloudflareAPIHandler = new berqCloudflareAPIHandler($email, $apitoken, $zoneid);
1871 $berqCloudflareAPIHandler->delete_rule_by_description('BerqWP cache rules');
1872 }
1873 }
1874
1875 function bwp_lock_cache_directory()
1876 {
1877 $cache_dir = optifer_cache;
1878
1879 if (!file_exists($cache_dir)) {
1880 wp_mkdir_p($cache_dir);
1881 }
1882
1883 $rules = <<<HTACCESS
1884 Order allow,deny
1885 Deny from all
1886
1887 <FilesMatch "index\.html\.gz$">
1888 Order allow,deny
1889 Allow from all
1890 </FilesMatch>
1891 HTACCESS;
1892
1893 file_put_contents($cache_dir . '.htaccess', $rules);
1894 }
1895
1896 function bwp_write_htaccess_rules($ignore_sandbox = false)
1897 {
1898 if (!got_mod_rewrite() || bwp_is_openlitespeed_server()) {
1899 return;
1900 }
1901
1902 if (!$ignore_sandbox && get_option('berqwp_enable_sandbox') == 1) {
1903 return;
1904 }
1905
1906 $htaccess = get_home_path() . '.htaccess';
1907 $rules = [
1908 '<IfModule mod_rewrite.c>',
1909 ' RewriteEngine On',
1910 ' RewriteCond %{REQUEST_METHOD} !POST',
1911 ' RewriteCond %{QUERY_STRING} ^$',
1912 ' RewriteCond %{HTTP_COOKIE} !(wp\-postpass|wordpress_logged_in|comment_author|woocommerce_cart_hash|edd_items_in_cart) [NC]',
1913 ' RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/berqwp/html/%{HTTP_HOST}%{REQUEST_URI}/index.html.gz -f',
1914 ' RewriteRule .* /wp-content/cache/berqwp/html/%{HTTP_HOST}%{REQUEST_URI}/index.html.gz [L]',
1915 '</IfModule>',
1916 '<IfModule mod_mime.c>',
1917 ' <FilesMatch "index\.html\.gz$">',
1918 ' ForceType text/html',
1919 ' AddEncoding gzip .gz',
1920 ' </FilesMatch>',
1921 '</IfModule>',
1922 '<IfModule mod_headers.c>',
1923 ' <FilesMatch "index\.html\.gz$">',
1924 ' Header set X-Served "server"',
1925 ' </FilesMatch>',
1926 '</IfModule>',
1927 ];
1928
1929 // Must appear before the WordPress block so Apache processes it first
1930 $marker = 'BerqWP Cache';
1931 $start = "# BEGIN $marker";
1932 $end = "# END $marker";
1933 $wp_start = '# BEGIN WordPress';
1934
1935 $block = $start . "\n";
1936 $block .= implode("\n", $rules) . "\n";
1937 $block .= $end . "\n";
1938
1939 $contents = file_exists($htaccess) ? file_get_contents($htaccess) : '';
1940
1941 // Remove all existing BerqWP Cache blocks (handles duplicates and corrupt state)
1942 $contents = preg_replace(
1943 '/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms',
1944 '',
1945 $contents
1946 );
1947
1948 // Insert before first WordPress block if present, otherwise prepend
1949 if (strpos($contents, $wp_start) !== false) {
1950 $contents = preg_replace('/' . preg_quote($wp_start, '/') . '/', $block . "\n" . $wp_start, $contents, 1);
1951 } else {
1952 $contents = $block . "\n" . $contents;
1953 }
1954
1955 file_put_contents($htaccess, $contents);
1956 }
1957
1958 function bwp_remove_htaccess_rules()
1959 {
1960 $htaccess = get_home_path() . '.htaccess';
1961
1962 if (!file_exists($htaccess)) {
1963 return;
1964 }
1965
1966 $contents = file_get_contents($htaccess);
1967 $contents = preg_replace('/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms', '', $contents);
1968 file_put_contents($htaccess, $contents);
1969 }
1970
1971 function bwp_sync_htaccess_on_sandbox_change()
1972 {
1973 // Action fires before update_option, so check the incoming POST value
1974 $sandbox_enabling = isset($_POST['berqwp_enable_sandbox']);
1975
1976 if ($sandbox_enabling) {
1977 bwp_remove_htaccess_rules();
1978 } else {
1979 bwp_write_htaccess_rules(true);
1980 }
1981 }
1982
1983 function berqwp_is_wp_cron_broken()
1984 {
1985 $cron = _get_cron_array();
1986 if (!$cron || !is_array($cron))
1987 return false;
1988
1989 $now = time();
1990 foreach ($cron as $timestamp => $events) {
1991 if ($timestamp < $now - 300) { // 5 minutes overdue
1992 return true;
1993 }
1994 }
1995 return false;
1996 }
1997
1998 function berqwp_is_page_url_excluded($page_url)
1999 {
2000 if (empty($page_url)) {
2001 return false;
2002 }
2003
2004 $pages_to_exclude = get_option('berq_exclude_urls', []);
2005
2006 if (strpos($page_url, '?') !== false) {
2007 $page_url = explode('?', $page_url)[0];
2008 }
2009
2010 if (in_array($page_url, $pages_to_exclude)) {
2011 return true;
2012 }
2013
2014 if (!empty($pages_to_exclude)) {
2015 foreach ($pages_to_exclude as $single_page_url) {
2016
2017 if (empty($single_page_url)) {
2018 continue;
2019 }
2020
2021 if (substr($single_page_url, -1) !== '*') {
2022 continue;
2023 }
2024
2025 $single_page_url = str_replace('*', '', $single_page_url);
2026
2027 if (strpos($page_url, $single_page_url) !== false) {
2028 return true;
2029 }
2030 }
2031 }
2032
2033 return false;
2034 }
2035
2036 // function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2037 // {
2038 // // Load the $buffer content into Simple HTML DOM
2039 // $html = HtmlDomParser::str_get_html($buffer);
2040
2041 // if (!$html) {
2042 // return $buffer;
2043 // }
2044
2045 // // Find the <body> tag
2046 // $body = $html->find('body', 0);
2047
2048 // // If the body tag exists, append the new HTML
2049 // if ($body) {
2050 // // Use htmlspecialchars to properly encode the inserted content
2051 // // Avoid breaking the HTML by not using innerhtml directly
2052 // $body->innertext .= $htmlToAppend;
2053 // } else {
2054 // // If no body tag exists, append the HTML to the document directly
2055 // $html->innertext .= $htmlToAppend;
2056 // }
2057
2058 // // Return the modified HTML content
2059 // return $html->save();
2060 // }
2061 //
2062 function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2063 {
2064 $pos = strripos($buffer, '</body>');
2065 if ($pos !== false) {
2066 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2067 }
2068
2069 // Fallback: try before </html>
2070 $pos = strripos($buffer, '</html>');
2071 if ($pos !== false) {
2072 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2073 }
2074
2075 // Last resort: append to end
2076 return $buffer . $htmlToAppend;
2077 }
2078
2079 function berqwp_appendHtmlToHead($buffer, $htmlToAppend)
2080 {
2081 $pos = stripos($buffer, '</head>');
2082 if ($pos !== false) {
2083 $buffer = substr_replace($buffer, PHP_EOL . $htmlToAppend, $pos, 0);
2084 }
2085
2086 // Fix missing </body> tag
2087 if (stripos($buffer, '</body>') === false) {
2088 $buffer = str_replace('</html>', '</body>' . PHP_EOL . '</html>', $buffer);
2089 }
2090
2091 return $buffer;
2092 }
2093
2094 function berqwp_prependHtmlToHead($buffer, $htmlToPrepend)
2095 {
2096 if (preg_match('/<head(\s[^>]*)?>/i', $buffer, $matches, PREG_OFFSET_CAPTURE)) {
2097 $insertPos = $matches[0][1] + strlen($matches[0][0]);
2098 return substr_replace($buffer, PHP_EOL . $htmlToPrepend, $insertPos, 0);
2099 }
2100
2101 // Fallback: no <head> tag found, return unchanged
2102 return $buffer;
2103 }
2104
2105 function berqwp_setup_dropin()
2106 {
2107 if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
2108 $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
2109 } else {
2110 $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
2111 }
2112
2113 // Remove advanced-cache.php when sandbox mode is enabled
2114 if (get_option('berqwp_enable_sandbox') == 1) {
2115
2116 if (file_exists($adv_cache_path)) {
2117 @unlink($adv_cache_path);
2118 }
2119
2120 return;
2121 }
2122
2123 $bwp_adv_cache = optifer_PATH . 'advanced-cache.php';
2124
2125 if (!file_exists($adv_cache_path) || (file_exists($adv_cache_path) && is_writable($adv_cache_path))) {
2126 if (
2127 (!file_exists($adv_cache_path) && get_option('berqwp_enable_sandbox') !== 1)
2128 || (file_exists($adv_cache_path) && md5_file($adv_cache_path) !== md5_file($bwp_adv_cache))
2129 ) {
2130
2131 // Dynamically create the drop-in file
2132 $dropin_content = file_get_contents(optifer_PATH . 'advanced-cache.php');
2133
2134 // Write the drop-in content to the file, replacing any existing file
2135 file_put_contents($adv_cache_path, $dropin_content);
2136
2137 // Enable wp cache in wp-config.php
2138 berqwp_enable_advanced_cache(true);
2139 }
2140 }
2141
2142
2143 // if (get_option('berqwp_enable_sandbox') == 1 && file_exists($adv_cache_path)) {
2144 // unlink($adv_cache_path);
2145 // }
2146 }
2147
2148 function berqwp_is_file_url($url)
2149 {
2150 $parsed = parse_url($url);
2151 if (!isset($parsed['path']))
2152 return false;
2153
2154 $extension = pathinfo($parsed['path'], PATHINFO_EXTENSION);
2155
2156 // allow html pages
2157 if (strtolower($extension) === 'html' || strtolower($extension) === 'htm') {
2158 return false;
2159 }
2160
2161 // Returns true if there is a file extension, false otherwise
2162 return !empty($extension);
2163 }
2164
2165 function berqwp_clear_cache_queue()
2166 {
2167 $berqconfigs = berqConfigs::getInstance();
2168 $site_id = $berqconfigs->get_configs()['site_id'];
2169 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
2170 $berqwp->clear_cache_queue(home_url(), $site_id);
2171 }
2172
2173 function berqwp_sync_addons($license_key, $site_url)
2174 {
2175 if (empty($license_key) || empty($site_url)) {
2176 return false;
2177 }
2178
2179 $post_data = [
2180 'license_key' => $license_key,
2181 'site_url' => $site_url,
2182 ];
2183
2184 $args = array(
2185 'body' => wp_json_encode($post_data),
2186 'method' => 'POST',
2187 'timeout' => 20,
2188 'headers' => array(
2189 'Content-Type' => 'application/json',
2190 ),
2191 );
2192
2193 global $berq_log;
2194 $berq_log->info("Trigger addon sync");
2195
2196 $response = wp_remote_post('https://berqwp.com/wp-json/berqwp/active-addons', $args);
2197 $response_body = wp_remote_retrieve_body($response);
2198 $json = json_decode($response_body, true);
2199
2200 if (empty($json) || $json['status'] !== 'success') {
2201 return false;
2202 }
2203
2204 if (is_array($json['active_addons'])) {
2205
2206 $enable_fluid_images = in_array('fluid_images', $json['active_addons']);
2207 if ($enable_fluid_images && !get_option('berqwp_can_use_fluid_images')) {
2208 update_option('berqwp_can_use_fluid_images', 1);
2209 update_option('bwp_require_flush_cache', 1);
2210 } elseif (!$enable_fluid_images) {
2211 update_option('berqwp_can_use_fluid_images', 0);
2212 }
2213 }
2214 }
2215
2216 function berqwp_validate_url_array($urls)
2217 {
2218 $home_host = parse_url(home_url(), PHP_URL_HOST);
2219 $home_scheme = parse_url(home_url(), PHP_URL_SCHEME);
2220
2221 $urls = array_filter($urls, function ($url) use ($home_host) {
2222 $url_host = parse_url($url, PHP_URL_HOST);
2223
2224 // Relative URLs → treat as same host
2225 if (empty($url_host)) {
2226 return true;
2227 }
2228
2229 return $url_host === $home_host;
2230 });
2231
2232 $urls = array_map(function ($url) use ($home_scheme) {
2233 // Skip invalid URLs
2234 if (!is_string($url) || $url === '') {
2235 return $url;
2236 }
2237
2238 $parts = parse_url($url);
2239
2240 // Relative URL → leave as-is
2241 if (empty($parts['scheme'])) {
2242 return $url;
2243 }
2244
2245 // Rebuild URL with home scheme
2246 $parts['scheme'] = $home_scheme;
2247
2248 $new_url = $parts['scheme'] . '://';
2249 $new_url .= $parts['host'] ?? '';
2250 $new_url .= isset($parts['port']) ? ':' . $parts['port'] : '';
2251 $new_url .= $parts['path'] ?? '';
2252 $new_url .= isset($parts['query']) ? '?' . $parts['query'] : '';
2253 $new_url .= isset($parts['fragment']) ? '#' . $parts['fragment'] : '';
2254
2255 return $new_url;
2256 }, $urls);
2257
2258 return $urls;
2259 }
2260
2261 function berqwp_render_toggle($name, $checked) {
2262 ?>
2263 <label class="berq-toggle">
2264 <input type="checkbox" name="<?php echo esc_attr($name); ?>" <?php checked(1, $checked, true); ?>>
2265 <div class="berq-toggle-switch"></div>
2266 </label>
2267 <?php
2268 }
2269
2270 function bwp_log_recently_optimized_page($page_url)
2271 {
2272 if (empty($page_url)) return;
2273
2274 // berqwp_stored_page_cache passes a slug/path, not a full URL — reconstruct it
2275 if (!str_starts_with($page_url, 'http')) {
2276 $page_url = home_url($page_url);
2277 }
2278 $page_url = urldecode($page_url);
2279
2280 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2281 if (!is_array($log)) $log = [];
2282
2283 // Remove existing entry for this URL so re-caching moves it to the top
2284 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2285
2286 array_unshift($log, [
2287 'url' => esc_url_raw($page_url),
2288 'cached_at' => current_time('Y-m-d H:i:s'),
2289 ]);
2290
2291 if (count($log) > 200) {
2292 $log = array_slice($log, 0, 200);
2293 }
2294
2295 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2296 }
2297
2298 function bwp_clear_recently_optimized_log()
2299 {
2300 delete_option('berqwp_recently_optimized');
2301 }
2302
2303 function bwp_remove_recently_optimized_entry($page_url)
2304 {
2305 if (empty($page_url)) return;
2306
2307 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2308 if (!is_array($log)) return;
2309
2310 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2311 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2312 }
2313
2314 function berqwp_can_use_cloud() {
2315 $berqconfigs = berqConfigs::getInstance();
2316 $configs = $berqconfigs->get_configs();
2317 $license_key = berqwp_get_license_key();
2318
2319 return !empty($configs['site_id']) && !empty($configs['secret']) && !empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && !empty($license_key);
2320 }
2321