PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.0.22
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.0.22
4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.0.30 4.0.29 4.0.28 4.0.27 4.0.26 4.0.24 4.0.25 4.0.23 4.0.22 4.0.21 4.0.19 4.0.18 4.0.17 4.0.16 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 All 169 releases
searchpro / inc / helper-functions.php

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

2,322 lines 69.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('bqwp_hide_feedback_notice', true, 60 * 60);
87 set_transient('berqwp_redirect', true, 1);
88 }
89 update_option('berqwp_sync_addons', true);
90 do_action('berqwp_activate_plugin');
91 }
92
93 function berqwp_is_slug_excludable($slug)
94 {
95 // Allow empty slug
96 // if (empty($slug)) {
97 // return true;
98 // }
99
100 $exclude_items = [
101 "favicon.ico",
102 "moderation-hash=",
103 "elementor_library=",
104 "add_to_wishlist=",
105 "robots.txt",
106 ".php",
107 "run_warmup=",
108 "et-compare-page=",
109 "add_to_compare=",
110 "min_price=",
111 "max_price=",
112 "view_mode=",
113 "view_mode_smart=",
114 "et_columns-count=",
115 "add_to_wishlist=",
116 "et-wishlist-page=",
117 "remove_wishlist=",
118 "stock_status=",
119 "page_id=",
120 "?p=",
121 "add-to-cart=",
122 "remove_item=",
123 "cart_item_key=",
124 "quantity=",
125 "my-account=",
126 "lost-password=",
127 "reset-password=",
128 "order-pay=",
129 "order-received=",
130 "view-order=",
131 "?s=",
132 "&s=",
133 "orderby=",
134 "product_tag=",
135 "product_cat=",
136 "min_price=",
137 "max_price=",
138 "rating=",
139 "filter_",
140 "edd_action=",
141 "download_id=",
142 "bbp_=",
143 "bbp-search=",
144 "&bp_=",
145 "?bp_=",
146 "?action=",
147 "&action=",
148 "gf_page=",
149 "gf_token=",
150 "gform_submit=",
151 "wordfence_logHuman=",
152 "wordfence_lh=",
153 "wordfence_syncAttackData=",
154 "?elementor=",
155 "&elementor=",
156 "?ld_=",
157 "&ld_=",
158 "lesson_id=",
159 "quiz_id=",
160 "wpforms=",
161 "?ref=",
162 "&ref=",
163 "?aff=",
164 "&aff=",
165 "?llms_=",
166 "&llms_=",
167 "lesson_id=",
168 "course_id=",
169 "vc_editable=true",
170 "et_fb=1",
171 "et_fb_edit",
172 "rcp_action=",
173 "member_id=",
174 "give_action=",
175 "donation_id=",
176 "giveDonationFormInIframe=",
177 "tribe-bar-date=",
178 "tribe_eventcategory=",
179 "tribe_paged=",
180 "tribe_organizer=",
181 "tribe_venue=",
182 "job_id=",
183 "job_applications=",
184 "job_alerts=",
185 "mepr-process=",
186 "mepr-transaction=",
187 "mepr_coupon=",
188 "popmake=",
189 "pum_form_sub=",
190 "pum_action=",
191 "ngg_page=",
192 "gallery_id=",
193 "pid=",
194 "wp_simple_pay=",
195 "sp_dont_optimize=",
196 "remove_from_wishlist=",
197 "action=yith-woocompare",
198 "?amp=",
199 "&amp=",
200 "?noamp=",
201 "&noamp=",
202 "subscription_id=",
203 "renewal_order=",
204 "edd_bk=",
205 "wpgmza=",
206 "poll_id=",
207 "pollresult=",
208 "swpquery=",
209 "swpengine=",
210 "monsterinsights=",
211 "defender=",
212 "wdf_scan=",
213 "wc_bookings_field=",
214 "wc_bookings_calendar=",
215 "?na=",
216 "&na=",
217 "?newsletter=",
218 "&newsletter=",
219 "affwp_ref=",
220 "affwp_campaign=",
221 "hmwp_token=",
222 "/page/",
223 "/search/",
224 "?",
225 "/embed",
226 "/view-order/",
227 "/redirect/",
228 "/elementskit-content/",
229 ];
230
231 $exclude_items = apply_filters('berqwp_exclude_slug_match', $exclude_items);
232
233 foreach ($exclude_items as $item) {
234 if (strpos($slug, $item) !== false) {
235 return true;
236 }
237 }
238
239 return false;
240 }
241
242 function berqwp_get_page_params($page_url, $is_forced = false)
243 {
244
245 if (empty($page_url)) {
246 return;
247 }
248
249 $page_slug = bwp_url_into_path($page_url);
250 $key = '';
251 $berqconfigs = berqConfigs::getInstance();
252 $berqwp_configs = $berqconfigs->get_configs();
253
254 $optimization_mode = get_option('berq_opt_mode');
255
256 if ($optimization_mode == 4) {
257 $optimization_mode = 'aggressive';
258 } elseif ($optimization_mode == 3) {
259 $optimization_mode = 'blaze';
260 } elseif ($optimization_mode == 2) {
261 $optimization_mode = 'medium';
262 } elseif ($optimization_mode == 1) {
263 $optimization_mode = 'basic';
264 }
265
266 // Data to send as POST parameters
267 $post_data = array(
268 'site_id' => $berqwp_configs['site_id'],
269 'license_key' => berqwp_get_license_key(),
270 'page_url' => $page_url,
271 'page_slug' => $page_slug,
272 'site_url' => home_url(),
273 'webp_max_width' => (int) get_option('berqwp_webp_max_width'),
274 'webp_quality' => (int) get_option('berqwp_webp_quality'),
275 'fluid_images' => get_option('berqwp_fluid_images'),
276 'enable_critical_css' => get_option('berqwp_enable_critical_css'),
277 'force_include_critical_css'=> get_option('berqwp_force_include_critical_css', []),
278 'exclude_css' => get_option('berqwp_exclude_css', []),
279 'exclude_js' => get_option('berqwp_exclude_js', []),
280 'exclude_third_party_js' => get_option('berqwp_exclude_third_party_js', []),
281 'async_excluded_styles' => get_option('berqwp_async_excluded_styles'),
282 'prerender_link' => get_option('berqwp_prerender_on_hover'),
283 'defer_excluded_js' => get_option('berqwp_defer_excluded_js'),
284 'delay_third_party_scripts' => get_option('berqwp_delay_third_party_scripts'),
285 'lazy_render' => get_option('berqwp_lazy_render'),
286 'img_lazyloading' => get_option('berqwp_image_lazyloading'),
287 'exclude_img_lazy_load' => get_option('berqwp_exclude_lazy_load_images', []),
288 'lazy_load_videos' => get_option('berqwp_lazy_load_videos'),
289 'youtube_lazyloading' => get_option('berqwp_lazyload_youtube_embed'),
290 'preload_yt_poster' => get_option('berqwp_preload_yt_poster'),
291 'js_mode' => get_option('berqwp_javascript_execution_mode'),
292 'key' => $key,
293 // 'interaction_delay' => get_option('berqwp_interaction_delay'),
294 'use_cdn' => get_option('berqwp_enable_cdn'),
295 'opt_mode' => $optimization_mode,
296 'disable_webp' => !get_option('berqwp_enable_webp'),
297 // 'js_css_exclude_urls' => get_option('berq_exclude_js_css', []),
298 'preload_fontfaces' => get_option('berqwp_preload_fontfaces'),
299 'enable_cwv' => get_option('berqwp_enable_cwv'),
300 'preload_cookiebanner' => get_option('berqwp_preload_cookiebanner'),
301 'css_optimization' => get_option('berq_css_optimization'),
302 'js_optimization' => get_option('berq_js_optimization'),
303 'exclude_cdn' => get_option('berq_exclude_cdn', []),
304 'webhook' => true,
305 'version' => BERQWP_VERSION
306 );
307
308 if (!get_option('berqwp_can_use_fluid_images')) {
309 $post_data['fluid_images'] = 0;
310 }
311
312 // if (defined('BERQ_STAGING')) {
313 // $post_data['run_queue'] = 1;
314 // $post_data['doing_queue'] = true;
315 // }
316
317 if ($page_url == home_url('/')) {
318 $is_forced = true;
319 }
320
321 if ($is_forced) {
322 $post_data['force'] = true;
323 }
324
325 return $post_data;
326 }
327
328 function bwp_pass_account_requirement()
329 {
330 global $berq_log;
331 $berqWP = \berqWP::getInstance();
332
333 $license_key = berqwp_get_license_key();
334
335 if (empty($license_key)) {
336 return false;
337 }
338
339 global $berq_log;
340 // $berq_log->info("License key check from bwp_pass_account_requirement function.");
341
342 $key_response = $berqWP->verify_license_key($license_key);
343
344 if (empty($key_response->product_ref)) {
345 return false;
346 }
347
348 if ($key_response->result !== 'success' || $key_response->status !== 'active') {
349 // $berq_log->error("account requirement: license verification failed");
350 return false;
351 }
352
353 if ($key_response->product_ref == 'Free Account' && bwp_cached_pages_count() >= 10) {
354 return false;
355 }
356
357 if ($key_response->product_ref == 'Starter' && bwp_cached_pages_count() >= 100) {
358 return false;
359 }
360
361 return true;
362 }
363
364 function warmup_cache_by_url($page_url, $is_forced = false, $async = false)
365 {
366
367 if (!berqwp_can_use_cloud()) {
368 return;
369 }
370
371 if (empty($page_url)) {
372 return;
373 }
374
375 if (empty(berqwp_get_license_key())) {
376 return;
377 }
378
379 if (berq_is_localhost() && !defined('BERQWP_ALLOW_LOCALHOST')) {
380 return;
381 }
382
383 if (berq_is_localhost() && defined('BERQWP_ALLOW_LOCALHOST') && BERQWP_ALLOW_LOCALHOST === false) {
384 return;
385 }
386
387 $page_url = strtolower($page_url);
388
389 $parsed_site_url = wp_parse_url(home_url());
390 $parsed_page_url = wp_parse_url($page_url);
391
392 if ($parsed_page_url['host'] !== $parsed_site_url['host']) {
393 return;
394 }
395
396 // Avoid caching file URLs e.g .php
397 // Allow .html
398 if (berqwp_is_file_url($page_url)) {
399 return;
400 }
401
402 // We don't want to cache page URL with query params
403 if (strpos($page_url, '?') !== false) {
404 return;
405 }
406
407 // Return if page url or page path (slug) is excluded
408 if (!bwp_can_optimize_page_url($page_url)) {
409 return;
410 }
411
412 global $berq_log;
413 $berq_log->info('warming page: ' . $page_url);
414
415 // Prepare post data for this page
416 $post_data = berqwp_get_page_params($page_url, $is_forced);
417
418 $berqwp = new BerqWP(get_option('berqwp_license_key'), null, optifer_cache);
419 $timeout = 30;
420
421 if ($async) {
422 $timeout = 1;
423 }
424
425 $berqwp->request_cache($post_data, $timeout);
426 }
427
428 function bwp_is_home_cached()
429 {
430 $cache_directory = bwp_get_cache_dir();
431 $cache_file = $cache_directory . bwp_build_cache_path(bwp_admin_home_url('/')) . '/index.html.gz';
432
433 return file_exists($cache_file);
434 }
435
436 function bwp_cache_current_page()
437 {
438 global $bwp_current_page, $berq_log;
439
440 // $berq_log->info('Shutdown hook fired.');
441
442 if (empty($bwp_current_page)) {
443 return;
444 }
445
446 // Check if the current user is logged in or if it's a POST request
447 if (is_user_logged_in() || $_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PURGE') {
448 return;
449 }
450
451 if (is_admin()) {
452 return;
453 }
454
455 if (defined('DOING_CRON') && DOING_CRON) {
456 return;
457 }
458
459 if (defined('DOING_AJAX') && DOING_AJAX) {
460 return;
461 }
462
463 if (defined('REST_REQUEST') && REST_REQUEST) {
464 return;
465 }
466
467 if (php_sapi_name() === 'cli' || defined('WP_CLI')) {
468 return;
469 }
470
471 if (!bwp_is_webpage()) {
472 return;
473 }
474
475 if (!bwp_can_warmup_cache($bwp_current_page)) {
476 return;
477 }
478
479 warmup_cache_by_url($bwp_current_page, false, false);
480 $bwp_current_page = null;
481 }
482
483 function bwp_is_partial_cache($identifier)
484 {
485
486 $cache_directory = bwp_get_cache_dir();
487 $cache_key = md5($identifier);
488 $cache_file = $cache_directory . $cache_key . '.html';
489
490 if (file_exists($cache_file)) {
491 $buffer = file_get_contents($cache_file);
492
493 if (!empty($buffer)) {
494 $html = HtmlDomParser::str_get_html($buffer);
495
496 if ($html !== false) {
497 $style_tag = $html->find('style#berqwp-critical-css', 0);
498
499 if ($style_tag === null) {
500 return true;
501 }
502 }
503 }
504 }
505
506 return false;
507 }
508
509 function berq_is_localhost()
510 {
511 $whitelist = array('127.0.0.1', '::1');
512
513 if (in_array($_SERVER['REMOTE_ADDR'], $whitelist)) {
514 return true;
515 }
516
517 return false;
518 }
519
520 function berqwp_remove_ignore_params($slug)
521 {
522 // List of tracking parameters to remove
523 $tracking_params = get_option('berq_ignore_urls_params', []);
524
525 $tracking_params = apply_filters('berqwp_ignored_urls_params', $tracking_params);
526
527 // Parse the provided slug
528 $url_parts = parse_url($slug);
529
530 // Get the current URL parameters
531 $url_params = array();
532 if (isset($url_parts['query'])) {
533 parse_str($url_parts['query'], $url_params);
534 }
535
536 // Remove specified tracking parameters from the URL
537 foreach ($tracking_params as $param) {
538 $param = trim($param);
539 if (isset($url_params[$param])) {
540 unset($url_params[$param]);
541 }
542 }
543
544 // Build the new query string
545 $new_query_string = http_build_query($url_params);
546
547 // Reconstruct the URL with the new query string
548 $updated_url = $url_parts['scheme'] . "://" . $url_parts['host'] . $url_parts['path'];
549 if (!empty($new_query_string)) {
550 $updated_url .= '?' . $new_query_string;
551 }
552
553 return $updated_url;
554 }
555
556 function berqwp_is_sub_dir_wp()
557 {
558 // // remove http
559 // $site_url = explode('//', home_url())[1];
560 // $break_slash = explode('/', $site_url);
561
562 // return count($break_slash) > 1;
563
564 $site_url = site_url();
565 $home_url = home_url();
566
567 // Parse paths from URLs
568 $site_path = @trim(parse_url($site_url, PHP_URL_PATH), '/');
569 $home_path = @trim(parse_url($home_url, PHP_URL_PATH), '/');
570
571 // Split paths into segments
572 $site_segments = explode('/', $site_path);
573 $home_segments = explode('/', $home_path);
574
575 // Check if Site URL has any segments and is not empty
576 if (!empty($site_path) && count($site_segments) > 0) {
577 // If both paths are the same and contain at least one segment
578 if ($site_path === $home_path && count($site_segments) > 0) {
579 return true; // WordPress is in a subdirectory
580 }
581 }
582
583 // Check if the Home URL has more segments than the Site URL
584 if (count($home_segments) > 1) {
585 return true; // Home URL indicates a subdirectory
586 }
587
588 return false; // Otherwise, it's likely not in a subdirectory
589 }
590
591 function berqwp_current_page_cache_file()
592 {
593 $slug_uri = $_SERVER['REQUEST_URI'];
594
595 // if wordpress is installed in a sub directory
596 if (berqwp_is_sub_dir_wp()) {
597 // Parse strings to extract paths
598 $path1 = explode('/', parse_url(home_url(), PHP_URL_PATH));
599 $path2 = explode('/', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
600
601 // Find the common part of the paths
602 $commonPath = implode('/', array_intersect($path1, $path2));
603
604 // Subtract the common part from the first string
605 $slug_uri = str_replace($commonPath, '', $_SERVER['REQUEST_URI']);
606 }
607
608 // Return if page is excluded from cache
609 $pages_to_exclude = get_option('berq_exclude_urls', []);
610
611 if (in_array(get_site_url() . $slug_uri, $pages_to_exclude)) {
612 return;
613 }
614
615
616 $slug = berqwp_remove_ignore_params($slug_uri);
617
618 if (isset($_GET['creating_cache'])) {
619 return;
620 }
621
622 if (get_option('berqwp_enable_sandbox') == 1 && isset($_GET['berqwp'])) {
623 $slug = explode('?berqwp', $slug_uri)[0];
624 } elseif (get_option('berqwp_enable_sandbox') == 1 && !isset($_GET['creating_cache'])) {
625 return;
626 }
627
628
629 // Attempt to retrieve the cached HTML from the cache directory
630 $cache_directory = bwp_get_cache_dir();
631
632 // Generate a unique cache key based on the current page URL
633 $cache_key = md5($slug);
634 $cache_file = $cache_directory . $cache_key . '.html';
635
636 return $cache_file;
637 }
638
639 function berqwp_get_LCP_details($url, $device = 'mobile')
640 {
641 $google_pagespeed_api_url = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=$url&strategy=$device ";
642
643 // Send a GET request to the Google PageSpeed Insights API
644 // $response = wp_remote_get($google_pagespeed_api_url, array('timeout' => 60));
645 $response = bwp_wp_remote_get($google_pagespeed_api_url, array('timeout' => 60));
646
647 if (is_wp_error($response)) {
648 return 'Error: ' . $response->get_error_message();
649 }
650
651 // Convert the JSON response to a PHP array
652 $body = wp_remote_retrieve_body($response);
653 $output = json_decode($body, true);
654
655 // Get the LCP data
656 return $output['lighthouseResult']['audits']['largest-contentful-paint-element']['details']['items'][0]['items'][0]['node'];
657 }
658
659 function berqwp_enable_advanced_cache($status)
660 {
661 global $berq_log;
662
663 // Get wp-config.php path
664 $configFilePath = defined('BERQWP_WP_CONFIG') ? BERQWP_WP_CONFIG : ABSPATH . 'wp-config.php';
665
666 // Check if the file exists
667 if (!file_exists($configFilePath)) {
668 $berq_log->error("Error: wp-config.php does not exist.");
669 return false;
670 }
671
672 // Check if the file is writable
673 if (!is_writable($configFilePath)) {
674 $berq_log->error("Error: wp-config.php is not writable. Check file permissions.");
675 return false;
676 }
677
678 // Prepare the new WP_CACHE definition
679 $newVal = sprintf("define( 'WP_CACHE', %s ); /* Added by BerqWP */\n", ($status ? "true" : "false"));
680 $replacementVal = sprintf(" %s ", ($status ? "true" : "false"));
681
682 // Read the file into an array of lines
683 $lines = file($configFilePath);
684 if ($lines === false || empty($lines)) {
685 $berq_log->error("Error: Could not read wp-config.php or file is empty.");
686 return false;
687 }
688
689 $wpCacheFound = false;
690 $phpOpeningTagLine = false;
691
692 // Process each line to update an existing WP_CACHE definition
693 foreach ($lines as $lineIndex => &$line) {
694 if (strpos($line, "<?php") !== false && strpos($line, "?>") === false) {
695 $phpOpeningTagLine = $lineIndex;
696 }
697 if (!$wpCacheFound && preg_match("/define\s*\(\s*['\"](.*?)['\"]\s*,(.*?)\)/", $line, $matches)) {
698 if ($matches[1] === "WP_CACHE") {
699 // Replace the existing WP_CACHE value with the new value
700 $line = str_replace($matches[2], $replacementVal, $line);
701 $wpCacheFound = true;
702 }
703 }
704 // If we have found both the opening tag and WP_CACHE, we can stop processing further lines.
705 if ($phpOpeningTagLine !== false && $wpCacheFound !== false) {
706 break;
707 }
708 }
709 unset($line);
710
711 // If WP_CACHE was not found and we are enabling it, insert it after the PHP opening tag.
712 if (!$wpCacheFound && $status) {
713 if ($phpOpeningTagLine !== false) {
714 array_splice($lines, $phpOpeningTagLine + 1, 0, [$newVal]);
715 } else {
716 // If no PHP opening tag is found, prepend a new one with the WP_CACHE definition.
717 array_unshift($lines, "<?php " . trim($newVal) . " ?>\n");
718 }
719 }
720
721 // Rebuild the file content
722 $newContent = implode("", $lines);
723 $tempFile = $configFilePath . '.tmp';
724
725 // Write to a temporary file first (atomic write)
726 if (file_put_contents($tempFile, $newContent, LOCK_EX) === false) {
727 $berq_log->error("Error: Could not write to temporary file: $tempFile");
728 return false;
729 }
730
731 // Rename the temporary file to wp-config.php
732 if (!rename($tempFile, $configFilePath)) {
733 $berq_log->error("Error: Could not rename temporary file to wp-config.php");
734 unlink($tempFile);
735 return false;
736 }
737
738 $berq_log->info("wp-config.php updated successfully.");
739 return true;
740 }
741
742 // Copied from Nginx Helper plugin
743 // function berqwp_unlink_recursive($dir)
744 // {
745
746 // if (!is_dir($dir)) {
747 // return;
748 // }
749
750 // $dh = opendir($dir);
751
752 // if (!$dh) {
753 // return;
754 // }
755
756 // // phpcs:ignore -- WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition -- Variable assignment required for recursion.
757 // while (false !== ($obj = readdir($dh))) {
758
759 // if ('.' === $obj || '..' === $obj) {
760 // continue;
761 // }
762
763 // if (!@unlink($dir . '/' . $obj)) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
764 // berqwp_unlink_recursive($dir . '/' . $obj, false);
765 // }
766 // }
767
768 // closedir($dh);
769 // }
770
771 function berqwp_unlink_recursive($dir)
772 {
773 if (!is_dir($dir)) {
774 return false;
775 }
776
777 $files = new RecursiveIteratorIterator(
778 new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
779 RecursiveIteratorIterator::CHILD_FIRST
780 );
781
782 foreach ($files as $file) {
783 if ($file->isFile()) {
784 if (!unlink($file->getRealPath())) {
785 error_log("Failed to delete file: " . $file->getRealPath());
786 }
787 } elseif ($file->isDir()) {
788 if (!rmdir($file->getRealPath())) {
789 error_log("Failed to delete directory: " . $file->getRealPath());
790 }
791 }
792 }
793
794 return rmdir($dir); // remove the root directory itself
795 }
796
797 function berqwp_get_last_modified_timestamp()
798 {
799 global $post;
800
801 // Check if it's a single post or page
802 if (is_singular()) {
803 return get_the_modified_time('U', $post->ID); // 'U' format parameter returns Unix timestamp
804 }
805
806 // Check if it's a taxonomy term
807 if (is_tax() || is_category() || is_tag()) {
808 $term = get_queried_object(); // Get the current term object
809
810 // For tags, get the last modified date of the most recent post associated with the tag
811 if (is_tag()) {
812 $args = array(
813 'tag_id' => $term->term_id,
814 'posts_per_page' => 1,
815 'orderby' => 'modified',
816 'order' => 'DESC',
817 'fields' => 'ids', // Return only post IDs to reduce overhead
818 );
819 $posts = get_posts($args);
820 if ($posts) {
821 $latest_post_id = $posts[0];
822 return get_the_modified_time('U', $latest_post_id); // 'U' format parameter returns Unix timestamp
823 }
824 }
825
826 // For category archives, get the last modified date of the most recent post within the category
827 if (is_category()) {
828 $args = array(
829 'category' => $term->term_id,
830 'posts_per_page' => 1,
831 'orderby' => 'modified',
832 'order' => 'DESC',
833 'fields' => 'ids', // Return only post IDs to reduce overhead
834 );
835 $posts = get_posts($args);
836 if ($posts) {
837 $latest_post_id = $posts[0];
838 return get_the_modified_time('U', $latest_post_id); // 'U' format parameter returns Unix timestamp
839 }
840 }
841
842 return strtotime($term->modified); // Convert modified date to timestamp
843 }
844
845 // Check if it's an archive
846 if (is_archive()) {
847 // For other archives
848 $archive_id = get_queried_object_id(); // Get the ID of the current archive
849 $archive = get_post($archive_id); // Get the archive post object
850 return strtotime($archive->post_modified); // Convert modified date to timestamp
851 }
852
853 // For other cases (fallback)
854 return false;
855 }
856
857 function bwp_is_gzip_supported()
858 {
859 return function_exists('gzencode') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false;
860 }
861
862 // function bwp_cached_pages_count()
863 // {
864 // $cache_directory = bwp_get_cache_dir();
865 // $cache_files = glob($cache_directory . "*.gz");
866 // return is_array($cache_files) ? count($cache_files) : 0;
867 // }
868
869 function bwp_cached_pages_count()
870 {
871 $cache_directory = bwp_get_cache_dir();
872
873 if (!is_dir($cache_directory)) {
874 return 0;
875 }
876
877 $count = 0;
878
879 $iterator = new RecursiveIteratorIterator(
880 new RecursiveDirectoryIterator($cache_directory, FilesystemIterator::SKIP_DOTS)
881 );
882
883 foreach ($iterator as $file) {
884 if ($file->isFile() && str_ends_with($file->getFilename(), '.gz')) {
885 $count++;
886 }
887 }
888
889 return $count;
890 }
891
892 function bwp_wp_remote_get($url, $args = array())
893 {
894 // Default arguments
895 $defaults = array(
896 'headers' => array(
897 'User-Agent' => 'BerqWP Bot', // Customize user agent if needed
898 ),
899 );
900
901 // Merge provided arguments with defaults
902 $args = wp_parse_args($args, $defaults);
903
904 if (empty($args['timeout'])) {
905 $args['timeout'] = 30;
906 }
907
908 // Initialize cURL session
909 $ch = curl_init();
910
911 // Set the URL
912 curl_setopt($ch, CURLOPT_URL, $url);
913
914 // Set to return the transfer as a string
915 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
916
917 // Set timeout in seconds
918 curl_setopt($ch, CURLOPT_TIMEOUT, $args['timeout']);
919
920 // Set the user-agent
921 curl_setopt($ch, CURLOPT_USERAGENT, $args['headers']['User-Agent']);
922
923 // Include header in the output
924 curl_setopt($ch, CURLOPT_HEADER, true);
925
926 // Execute the request
927 $response = curl_exec($ch);
928
929 // Check for errors
930 if (curl_errno($ch)) {
931 $error_message = curl_error($ch);
932 curl_close($ch);
933 return new WP_Error('curl_error', $error_message);
934 }
935
936 // Close cURL session
937 curl_close($ch);
938
939 // Separate headers and body
940 list($headers, $body) = explode("\r\n\r\n", $response, 2);
941
942 // Parse headers into array
943 $header_lines = explode("\r\n", $headers);
944 $headers = array();
945 foreach ($header_lines as $line) {
946 $parts = explode(':', $line, 2);
947 if (count($parts) == 2) {
948 $headers[trim($parts[0])] = trim($parts[1]);
949 }
950 }
951
952 // Construct response array similar to wp_remote_get
953 $response = array(
954 'headers' => $headers,
955 'body' => $body,
956 'response' => array(
957 'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
958 'message' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
959 ),
960 'cookies' => array(),
961 'filename' => '',
962 );
963
964 return $response;
965 }
966
967 function bwp_is_openlitespeed_server()
968 {
969 $is_litespeed = false;
970 $is_litespeed = isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false;
971
972 $headers = getallheaders();
973 foreach ($headers as $header => $value) {
974 if (stripos($value, 'LiteSpeed') !== false) {
975 $is_litespeed = true;
976 }
977 }
978
979 return $is_litespeed;
980 }
981
982 function verify_request_origin($request)
983 {
984 // Check the referrer header to ensure the request is coming from the same site
985 $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
986 $site_url = get_site_url();
987
988 // Optionally, check the origin header
989 $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
990
991 // Ensure the request comes from the same site
992 if (strpos($referrer, $site_url) !== 0 && strpos($origin, $site_url) !== 0) {
993 return new WP_Error('rest_forbidden', esc_html__('You cannot access this resource.', 'searchpro'), array('status' => 403));
994 }
995
996 // Ensure the origin is a subdomain of berqwp.com
997 if (!preg_match('/^https?:\/\/([a-z0-9-]+\.)?berqwp\.com$/', $origin)) {
998 return new WP_Error('rest_forbidden', esc_html__('You cannot access this resource.', 'searchpro'), array('status' => 403));
999 }
1000
1001 return true;
1002 }
1003
1004 function berq_rest_permission_callback(WP_REST_Request $request)
1005 {
1006 // Get the nonce from the request
1007 $nonce = $request->get_header('X-WP-Nonce');
1008 $hash = $request->get_header('X-berqwp-key-hash');
1009
1010 if ($hash == md5(berqwp_get_license_key())) {
1011 return true;
1012 }
1013
1014 // Verify the nonce
1015 if (!wp_verify_nonce($nonce, 'wp_rest')) {
1016 return new WP_Error('rest_invalid_nonce', __('Invalid nonce', 'searchpro'), array('status' => 403));
1017 }
1018
1019 return true; // Return true to allow the request
1020 }
1021
1022 function berq_rest_verify_license_callback(WP_REST_Request $request)
1023 {
1024 $license_key_hash = sanitize_text_field($request->get_param('license_key_hash'));
1025 $license_key = berqwp_get_license_key();
1026
1027 if (empty($license_key_hash) || empty($license_key) || $license_key_hash !== md5($license_key)) {
1028 global $berq_log;
1029 $berq_log->error("Exiting... Invalid license key.");
1030 return new WP_Error('rest_invalid_nonce', __('Invalid license key', 'searchpro'), array('status' => 403));
1031 }
1032
1033 return true; // Return true to allow the request
1034 }
1035
1036 function bwp_dash_notification($msg = '', $status = 'warning')
1037 {
1038 ?>
1039 <div class="berqwp-notification <?php echo esc_attr($status) ?>">
1040 <?php
1041
1042 echo "<div class='icon'>";
1043 if ($status == 'warning') {
1044 echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>';
1045 } elseif ($status == 'error') {
1046 echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c53 0 96 43 96 96l0 3.6c0 15.7-12.7 28.4-28.4 28.4l-135.1 0c-15.7 0-28.4-12.7-28.4-28.4l0-3.6c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4l112 0c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5l64.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-64 0c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6L272 240c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 239.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64.3 0c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"/></svg>';
1047 } elseif ($status == 'info') {
1048 echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M96 64c0-17.7-14.3-32-32-32S32 46.3 32 64l0 256c0 17.7 14.3 32 32 32s32-14.3 32-32L96 64zM64 480a40 40 0 1 0 0-80 40 40 0 1 0 0 80z"/></svg>';
1049 } else {
1050 return;
1051 }
1052 echo "</div>";
1053 echo esc_html($msg);
1054 ?>
1055 </div>
1056 <?php
1057 }
1058
1059 function bwp_can_warmup_cache($identifier)
1060 {
1061
1062 if (get_transient('bwp_warmup_lock_' . md5($identifier)) === false) {
1063
1064 set_transient('bwp_warmup_lock_' . md5($identifier), true, 100);
1065 return true;
1066 }
1067
1068 return false;
1069 }
1070
1071 function bwp_clear_warmup_lock($slug)
1072 {
1073 delete_transient('bwp_warmup_lock_' . md5($slug));
1074 }
1075
1076 function bwp_extractUrlsFromCss($cssContent)
1077 {
1078 $urls = [];
1079 $pattern = '/url\((.*?)\)/i';
1080
1081 preg_match_all($pattern, $cssContent, $matches);
1082
1083 if (!empty($matches[1])) {
1084 foreach ($matches[1] as $match) {
1085 // Trim any surrounding quotes and whitespace
1086 $urls[] = trim($match, '\'" ');
1087 }
1088 }
1089
1090 return $urls;
1091 }
1092
1093 function bwp_getBaseUrl($fileUrl)
1094 {
1095 // Parse the URL and get its components
1096 $parsedUrl = parse_url($fileUrl);
1097 $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] . '://' : '';
1098 $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
1099 $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
1100
1101 // Remove the file name from the path to get the base URL
1102 $basePath = preg_replace('/\/[^\/]+$/', '/', $path);
1103
1104 // Construct the base URL
1105 return $scheme . $host . $basePath;
1106 }
1107
1108 function bwp_rel2abs($rel, $base)
1109 {
1110 /* return if already absolute URL */
1111 if (parse_url($rel, PHP_URL_SCHEME) != '')
1112 return $rel;
1113
1114 /* queries and anchors */
1115 if ($rel[0] == '#' || $rel[0] == '?')
1116 return $base . $rel;
1117
1118 /* parse base URL and convert to local variables:
1119 $scheme, $host, $path */
1120 extract(parse_url($base));
1121
1122 /* remove non-directory element from path */
1123 $path = preg_replace('#/[^/]*$#', '', $path);
1124
1125 /* destroy path if relative url points to root */
1126 if ($rel[0] == '/')
1127 $path = '';
1128
1129 /* dirty absolute URL */
1130 $abs = "$host$path/$rel";
1131
1132 /* replace '//' or '/./' or '/foo/../' with '/' */
1133 $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
1134 for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
1135 }
1136
1137 /* absolute URL is ready! */
1138 return $scheme . '://' . $abs;
1139 }
1140
1141 function update_image_url_extension($image_url, $file_extension)
1142 {
1143 $url_arr = explode('.', $image_url);
1144 $last_index = count($url_arr) - 1;
1145
1146 // Extract the file extension
1147 $current_file_extension = pathinfo($image_url, PATHINFO_EXTENSION);
1148
1149 $url_arr[$last_index] = str_replace("$current_file_extension", $file_extension, $url_arr[$last_index]);
1150 $new_image_url = implode('.', $url_arr);
1151
1152 return $new_image_url;
1153 }
1154
1155 if (!function_exists('str_contains')) {
1156 function str_contains(string $haystack, string $needle): bool
1157 {
1158 return strpos($haystack, $needle) !== false;
1159 }
1160 }
1161
1162 function bwp_check_connection($short_live = false, $force = false)
1163 {
1164
1165 $transient_key = "berqwp_connection_status";
1166
1167 if ($force) {
1168 delete_transient('berqwp_connection_status');
1169 delete_transient('berqwp_connection_status_sl');
1170 }
1171
1172 // Allow cache busting by passing $force_check = true
1173 // if ($force_check) {
1174 // delete_transient($transient_key);
1175 // }
1176
1177 if ($short_live) {
1178 $transient_key = "berqwp_connection_status_sl";
1179 }
1180
1181 // Check if the result is cached
1182 $cached_status = get_transient($transient_key);
1183
1184 if ($cached_status) {
1185 return $cached_status;
1186 }
1187
1188 // Perform the actual REST API check
1189 $response = wp_safe_remote_get('https://boost.berqwp.com/photon/?connection_test=1&url=' . bwp_admin_home_url('/?utm_source=' . time()), ['timeout' => 60]);
1190
1191 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1192 return ['status' => 'success']; // Skip if server is unreachable
1193 // $result = array(
1194 // 'status' => 'error',
1195 // 'message' => 'The site is not accessible. Error: ' . $response->get_error_message(),
1196 // );
1197 } else {
1198 $body = wp_remote_retrieve_body($response);
1199
1200 if ($body == 'pingpong') {
1201 $result = array(
1202 'status' => 'success',
1203 'message' => 'Your website is accessible by BerqWP server.',
1204 );
1205 } else {
1206 $result = array(
1207 'status' => 'error',
1208 'message' => 'BerqWP server is unable to access your website, please whitelist our server IP address.',
1209 );
1210 }
1211 }
1212
1213 if ($short_live) {
1214 set_transient($transient_key, $result, 60 * 5);
1215 } else {
1216 set_transient($transient_key, $result, 60 * 60 * 24);
1217 }
1218
1219 return $result;
1220 }
1221
1222 function bwp_sendPostRequestInBackground($url, $params)
1223 {
1224 $userAgent = 'BerqWP';
1225 $urlParts = parse_url($url);
1226
1227 // Ensure we have a valid path and handle query strings
1228 $path = isset($urlParts['path']) ? $urlParts['path'] : '/';
1229 if (isset($urlParts['query'])) {
1230 $path .= '?' . $urlParts['query'];
1231 }
1232
1233 $postString = http_build_query($params);
1234
1235 $host = $urlParts['host'];
1236 $scheme = isset($urlParts['scheme']) ? $urlParts['scheme'] : 'http';
1237 $port = ($scheme === 'https') ? 443 : 80;
1238 $transport = ($scheme === 'https') ? 'ssl://' : '';
1239
1240 $request = "POST $path HTTP/1.1\r\n";
1241 $request .= "Host: $host\r\n";
1242 $request .= "User-Agent: $userAgent\r\n";
1243 $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
1244 $request .= "Content-Length: " . strlen($postString) . "\r\n";
1245 $request .= "Connection: Close\r\n\r\n";
1246 $request .= $postString;
1247
1248 $fp = fsockopen($transport . $host, $port, $errno, $errstr, 30);
1249
1250 if ($fp) {
1251 fwrite($fp, $request);
1252 fclose($fp); // Close immediately to prevent waiting for the response
1253 } else {
1254 // If fsockopen fails, fallback to wp_remote_post
1255 $args = array(
1256 'timeout' => 0.01,
1257 'headers' => array(
1258 'User-Agent' => $userAgent,
1259 ),
1260 'body' => $params
1261 );
1262
1263 wp_remote_post($url, $args);
1264 }
1265 }
1266
1267 function bwp_is_webpage()
1268 {
1269 $headers = headers_list();
1270 $contentType = '';
1271
1272 foreach ($headers as $header) {
1273 if (stripos($header, 'Content-Type') !== false) {
1274 $contentType = $header;
1275 break;
1276 }
1277 }
1278
1279 if (stripos($contentType, 'text/html') !== false) {
1280 return true;
1281 }
1282
1283 return false;
1284 }
1285
1286 function bwp_isGzipEncoded()
1287 {
1288 // Check if the Content-Encoding header is set to gzip
1289 if (isset($_SERVER['HTTP_CONTENT_ENCODING']) && strtolower($_SERVER['HTTP_CONTENT_ENCODING']) === 'gzip') {
1290 return true;
1291 }
1292
1293 foreach (headers_list() as $header) {
1294 if (stripos($header, 'Content-Encoding: gzip') !== false) {
1295 return true;
1296 }
1297 }
1298
1299 return false;
1300 }
1301
1302 function bwp_sluguri_into_path($slug_uri)
1303 {
1304 $is_multisite = function_exists('is_multisite') && is_multisite();
1305 $home_path = parse_url(home_url(), PHP_URL_PATH);
1306
1307 // if (berqwp_is_sub_dir_wp() && !$is_multisite) {
1308 if (!$is_multisite && $home_path !== null) {
1309 // Parse strings to extract paths
1310 $path1 = explode('/', $home_path);
1311 $path2 = explode('/', parse_url($slug_uri, PHP_URL_PATH));
1312
1313 // Find the common part of the paths
1314 $commonPath = implode('/', array_intersect($path1, $path2));
1315
1316 // Subtract the common part from the first string
1317 $slug_uri = str_replace($commonPath, '', $slug_uri);
1318 }
1319
1320 return $slug_uri;
1321 }
1322
1323 function bwp_url_into_path($url)
1324 {
1325 $parsed_url = parse_url($url);
1326 $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
1327 $query = isset($parsed_url['query']) ? $parsed_url['query'] : '';
1328 $homeurl = home_url();
1329
1330 if (!empty($query)) {
1331 $path = $path . '?' . $query;
1332 }
1333
1334 $is_multisite = function_exists('is_multisite') && is_multisite();
1335 $home_path = parse_url($homeurl, PHP_URL_PATH);
1336
1337 if (!$is_multisite && $home_path !== null) {
1338 // Parse strings to extract paths
1339 $path1 = explode('/', $home_path);
1340 $path2 = explode('/', parse_url($path, PHP_URL_PATH));
1341
1342 // Find the common part of the paths
1343 $commonPath = implode('/', array_intersect($path1, $path2));
1344
1345 // Subtract the common part from the first string
1346 $path = str_replace($commonPath, '', $path);
1347 }
1348
1349 // $path = bwp_intersect_str(bwp_admin_home_url(), $path);
1350
1351 return $path;
1352 }
1353
1354 function bwp_get_cache_dir()
1355 {
1356 $cache_directory = optifer_cache . 'html/';
1357
1358 if (function_exists('is_multisite') && is_multisite()) {
1359 $site_id = get_current_blog_id();
1360 $cache_directory .= 'site-' . $site_id . '/';
1361 }
1362
1363 if (!is_dir($cache_directory)) {
1364 wp_mkdir_p($cache_directory);
1365 }
1366
1367 return $cache_directory;
1368 }
1369
1370 function bwp_get_static_cache_dir()
1371 {
1372 $dir = optifer_cache . 'static/';
1373
1374 if (function_exists('is_multisite') && is_multisite()) {
1375 $dir .= 'site-' . get_current_blog_id() . '/';
1376 }
1377
1378 if (!is_dir($dir)) {
1379 wp_mkdir_p($dir);
1380 }
1381
1382 return $dir;
1383 }
1384
1385 function bwp_webhook()
1386 {
1387 require_once optifer_PATH . '/api/webhook.php';
1388 }
1389
1390 function bwp_update_configs_webhook()
1391 {
1392 require_once optifer_PATH . '/api/update_configs.php';
1393 }
1394
1395 function bwp_check_status_webhook()
1396 {
1397 require_once optifer_PATH . '/api/check_status.php';
1398 }
1399
1400 function bwp_handle_request_cache()
1401 {
1402 require_once optifer_PATH . '/api/request_cache.php';
1403 }
1404
1405 function bwp_get_translatepress_urls($page_url)
1406 {
1407 $trp = TRP_Translate_Press::get_trp_instance();
1408 $trp_settings = get_option('trp_settings', array());
1409 $languages = $trp->get_component('languages');
1410 $url_converter = $trp->get_component('url_converter');
1411 $publish_languages = $languages->get_language_names($trp_settings['publish-languages'], 'english_name');
1412 $urls = [];
1413
1414
1415 if (!empty($publish_languages)) {
1416 foreach ($publish_languages as $key => $value) {
1417 $translation_url = $url_converter->get_url_for_language($key, $page_url, '');
1418 $urls[] = $translation_url;
1419 }
1420 }
1421
1422 return $urls;
1423 }
1424
1425 function bwp_can_optimize_page_url($page_url)
1426 {
1427 $slug = bwp_url_into_path($page_url);
1428 if (berqwp_is_slug_excludable($slug)) {
1429 return false;
1430 }
1431
1432 if (berqwp_is_page_url_excluded($page_url)) {
1433 return false;
1434 }
1435
1436 return true;
1437 }
1438
1439 function bwp_get_sitemap()
1440 {
1441 if (isset($_GET['berqwp_sitemap'])) {
1442
1443
1444 // Get post types to optimize
1445 $post_types = get_option('berqwp_optimize_post_types');
1446
1447 $post_types = apply_filters('berqwp_warmup_post_types', $post_types);
1448 $rewrite_map = apply_filters('berqwp_warmup_post_types_rewrite_map', []);
1449
1450 // Set the number of posts per batch to handle
1451 $posts_per_page = 10000; // Adjust this based on server capacity
1452
1453 // Get current page from query string (for pagination)
1454 $paged = isset($_GET['paged']) ? intval($_GET['paged']) : 1;
1455
1456 // Build the query arguments with pagination
1457 $args = array(
1458 'post_type' => $post_types,
1459 'post_status' => array('publish'), // Only published posts
1460 'posts_per_page' => $posts_per_page,
1461 'paged' => $paged,
1462 'fields' => 'ids', // Only retrieve IDs to save memory
1463 );
1464
1465 // Run the query
1466 $query = new WP_Query($args);
1467 $total_posts = $query->found_posts; // Get the total number of posts
1468
1469 // Calculate total pages
1470 $total_pages = ceil($total_posts / $posts_per_page);
1471
1472 // Output JSON header
1473 header('Content-Type: application/json');
1474
1475 $post_params = berqwp_get_page_params('/');
1476 unset($post_params['license_key']);
1477 unset($post_params['page_slug']);
1478 unset($post_params['page_url']);
1479 $post_params['key'] = '';
1480
1481 $berqconfigs = berqConfigs::getInstance();
1482 $configs = $berqconfigs->get_configs();
1483 $post_params['cache_lifespan'] = $configs['cache_lifespan'];
1484
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;" ';
1644 } else {
1645 // echo ' style="display:none" ';
1646 echo ' style="visibility:hidden;opacity:0;height:0;overflow:hidden;" ';
1647 }
1648 } else {
1649
1650 if ($tab_id == $default_tab) {
1651 // echo ' style="display:block" ';
1652 echo ' style="visibility:visible;opacity:1;height:auto;" ';
1653 } else {
1654 // echo ' style="display:none" ';
1655 echo ' style="visibility:hidden;opacity:0;height:0;overflow:hidden;" ';
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 esc_html_e($status, 'searchpro'); ?></div>
1696 <?php if (!empty($title)) { ?>
1697 <h5><?php echo wp_kses_post(__($title, 'searchpro')); ?></h5>
1698 <?php } ?>
1699
1700 <?php if (!empty($message)) { ?>
1701 <?php echo wp_kses_post(__($message, 'searchpro')); ?>
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'], 'searchpro') ?? ''; ?>
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.', 'berqwp'));
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 HTACCESS;
1893
1894 file_put_contents($cache_dir . '.htaccess', $rules);
1895 }
1896
1897 function bwp_write_htaccess_rules($ignore_sandbox = false)
1898 {
1899 if (!got_mod_rewrite() || bwp_is_openlitespeed_server()) {
1900 return;
1901 }
1902
1903 if (!$ignore_sandbox && get_option('berqwp_enable_sandbox') == 1) {
1904 return;
1905 }
1906
1907 $htaccess = get_home_path() . '.htaccess';
1908 $rules = [
1909 '<IfModule mod_rewrite.c>',
1910 ' RewriteEngine On',
1911 ' RewriteCond %{REQUEST_METHOD} !POST',
1912 ' RewriteCond %{QUERY_STRING} ^$',
1913 ' RewriteCond %{HTTP_COOKIE} !(wp\-postpass|wordpress_logged_in|comment_author|woocommerce_cart_hash|edd_items_in_cart) [NC]',
1914 ' RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/berqwp/html/%{HTTP_HOST}%{REQUEST_URI}/index.html.gz -f',
1915 ' RewriteRule .* /wp-content/cache/berqwp/html/%{HTTP_HOST}%{REQUEST_URI}/index.html.gz [L]',
1916 '</IfModule>',
1917 '<IfModule mod_mime.c>',
1918 ' <FilesMatch "index\.html\.gz$">',
1919 ' ForceType text/html',
1920 ' AddEncoding gzip .gz',
1921 ' </FilesMatch>',
1922 '</IfModule>',
1923 '<IfModule mod_headers.c>',
1924 ' <FilesMatch "index\.html\.gz$">',
1925 ' Header set X-Served "server"',
1926 ' </FilesMatch>',
1927 '</IfModule>',
1928 ];
1929
1930 // Must appear before the WordPress block so Apache processes it first
1931 $marker = 'BerqWP Cache';
1932 $start = "# BEGIN $marker";
1933 $end = "# END $marker";
1934 $wp_start = '# BEGIN WordPress';
1935
1936 $block = $start . "\n";
1937 $block .= implode("\n", $rules) . "\n";
1938 $block .= $end . "\n";
1939
1940 $contents = file_exists($htaccess) ? file_get_contents($htaccess) : '';
1941
1942 // Remove all existing BerqWP Cache blocks (handles duplicates and corrupt state)
1943 $contents = preg_replace(
1944 '/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms',
1945 '',
1946 $contents
1947 );
1948
1949 // Insert before first WordPress block if present, otherwise prepend
1950 if (strpos($contents, $wp_start) !== false) {
1951 $contents = preg_replace('/' . preg_quote($wp_start, '/') . '/', $block . "\n" . $wp_start, $contents, 1);
1952 } else {
1953 $contents = $block . "\n" . $contents;
1954 }
1955
1956 file_put_contents($htaccess, $contents);
1957 }
1958
1959 function bwp_remove_htaccess_rules()
1960 {
1961 $htaccess = get_home_path() . '.htaccess';
1962
1963 if (!file_exists($htaccess)) {
1964 return;
1965 }
1966
1967 $contents = file_get_contents($htaccess);
1968 $contents = preg_replace('/^# BEGIN BerqWP Cache.*?^# END BerqWP Cache\s*/ms', '', $contents);
1969 file_put_contents($htaccess, $contents);
1970 }
1971
1972 function bwp_sync_htaccess_on_sandbox_change()
1973 {
1974 // Action fires before update_option, so check the incoming POST value
1975 $sandbox_enabling = isset($_POST['berqwp_enable_sandbox']);
1976
1977 if ($sandbox_enabling) {
1978 bwp_remove_htaccess_rules();
1979 } else {
1980 bwp_write_htaccess_rules(true);
1981 }
1982 }
1983
1984 function berqwp_is_wp_cron_broken()
1985 {
1986 $cron = _get_cron_array();
1987 if (!$cron || !is_array($cron))
1988 return false;
1989
1990 $now = time();
1991 foreach ($cron as $timestamp => $events) {
1992 if ($timestamp < $now - 300) { // 5 minutes overdue
1993 return true;
1994 }
1995 }
1996 return false;
1997 }
1998
1999 function berqwp_is_page_url_excluded($page_url)
2000 {
2001 if (empty($page_url)) {
2002 return false;
2003 }
2004
2005 $pages_to_exclude = get_option('berq_exclude_urls', []);
2006
2007 if (strpos($page_url, '?') !== false) {
2008 $page_url = explode('?', $page_url)[0];
2009 }
2010
2011 if (in_array($page_url, $pages_to_exclude)) {
2012 return true;
2013 }
2014
2015 if (!empty($pages_to_exclude)) {
2016 foreach ($pages_to_exclude as $single_page_url) {
2017
2018 if (empty($single_page_url)) {
2019 continue;
2020 }
2021
2022 if (substr($single_page_url, -1) !== '*') {
2023 continue;
2024 }
2025
2026 $single_page_url = str_replace('*', '', $single_page_url);
2027
2028 if (strpos($page_url, $single_page_url) !== false) {
2029 return true;
2030 }
2031 }
2032 }
2033
2034 return false;
2035 }
2036
2037 // function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2038 // {
2039 // // Load the $buffer content into Simple HTML DOM
2040 // $html = HtmlDomParser::str_get_html($buffer);
2041
2042 // if (!$html) {
2043 // return $buffer;
2044 // }
2045
2046 // // Find the <body> tag
2047 // $body = $html->find('body', 0);
2048
2049 // // If the body tag exists, append the new HTML
2050 // if ($body) {
2051 // // Use htmlspecialchars to properly encode the inserted content
2052 // // Avoid breaking the HTML by not using innerhtml directly
2053 // $body->innertext .= $htmlToAppend;
2054 // } else {
2055 // // If no body tag exists, append the HTML to the document directly
2056 // $html->innertext .= $htmlToAppend;
2057 // }
2058
2059 // // Return the modified HTML content
2060 // return $html->save();
2061 // }
2062 //
2063 function berqwp_appendHtmlToBody($buffer, $htmlToAppend)
2064 {
2065 $pos = strripos($buffer, '</body>');
2066 if ($pos !== false) {
2067 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2068 }
2069
2070 // Fallback: try before </html>
2071 $pos = strripos($buffer, '</html>');
2072 if ($pos !== false) {
2073 return substr_replace($buffer, $htmlToAppend, $pos, 0);
2074 }
2075
2076 // Last resort: append to end
2077 return $buffer . $htmlToAppend;
2078 }
2079
2080 function berqwp_appendHtmlToHead($buffer, $htmlToAppend)
2081 {
2082 $pos = stripos($buffer, '</head>');
2083 if ($pos !== false) {
2084 $buffer = substr_replace($buffer, PHP_EOL . $htmlToAppend, $pos, 0);
2085 }
2086
2087 // Fix missing </body> tag
2088 if (stripos($buffer, '</body>') === false) {
2089 $buffer = str_replace('</html>', '</body>' . PHP_EOL . '</html>', $buffer);
2090 }
2091
2092 return $buffer;
2093 }
2094
2095 function berqwp_prependHtmlToHead($buffer, $htmlToPrepend)
2096 {
2097 if (preg_match('/<head(\s[^>]*)?>/i', $buffer, $matches, PREG_OFFSET_CAPTURE)) {
2098 $insertPos = $matches[0][1] + strlen($matches[0][0]);
2099 return substr_replace($buffer, PHP_EOL . $htmlToPrepend, $insertPos, 0);
2100 }
2101
2102 // Fallback: no <head> tag found, return unchanged
2103 return $buffer;
2104 }
2105
2106 function berqwp_setup_dropin()
2107 {
2108 if (defined('BERQWP_ADVANCED_CACHE_PATH')) {
2109 $adv_cache_path = BERQWP_ADVANCED_CACHE_PATH;
2110 } else {
2111 $adv_cache_path = WP_CONTENT_DIR . '/advanced-cache.php';
2112 }
2113
2114 // Remove advanced-cache.php when sandbox mode is enabled
2115 if (get_option('berqwp_enable_sandbox') == 1) {
2116
2117 if (file_exists($adv_cache_path)) {
2118 @unlink($adv_cache_path);
2119 }
2120
2121 return;
2122 }
2123
2124 $bwp_adv_cache = optifer_PATH . 'advanced-cache.php';
2125
2126 if (!file_exists($adv_cache_path) || (file_exists($adv_cache_path) && is_writable($adv_cache_path))) {
2127 if (
2128 (!file_exists($adv_cache_path) && get_option('berqwp_enable_sandbox') !== 1)
2129 || (file_exists($adv_cache_path) && md5_file($adv_cache_path) !== md5_file($bwp_adv_cache))
2130 ) {
2131
2132 // Dynamically create the drop-in file
2133 $dropin_content = file_get_contents(optifer_PATH . 'advanced-cache.php');
2134
2135 // Write the drop-in content to the file, replacing any existing file
2136 file_put_contents($adv_cache_path, $dropin_content);
2137
2138 // Enable wp cache in wp-config.php
2139 berqwp_enable_advanced_cache(true);
2140 }
2141 }
2142
2143
2144 // if (get_option('berqwp_enable_sandbox') == 1 && file_exists($adv_cache_path)) {
2145 // unlink($adv_cache_path);
2146 // }
2147 }
2148
2149 function berqwp_is_file_url($url)
2150 {
2151 $parsed = parse_url($url);
2152 if (!isset($parsed['path']))
2153 return false;
2154
2155 $extension = pathinfo($parsed['path'], PATHINFO_EXTENSION);
2156
2157 // allow html pages
2158 if (strtolower($extension) === 'html' || strtolower($extension) === 'htm') {
2159 return false;
2160 }
2161
2162 // Returns true if there is a file extension, false otherwise
2163 return !empty($extension);
2164 }
2165
2166 function berqwp_clear_cache_queue()
2167 {
2168 $berqconfigs = berqConfigs::getInstance();
2169 $site_id = $berqconfigs->get_configs()['site_id'];
2170 $berqwp = new BerqWP(berqwp_get_license_key(), null, null);
2171 $berqwp->clear_cache_queue(home_url(), $site_id);
2172 }
2173
2174 function berqwp_sync_addons($license_key, $site_url)
2175 {
2176 if (empty($license_key) || empty($site_url)) {
2177 return false;
2178 }
2179
2180 $post_data = [
2181 'license_key' => $license_key,
2182 'site_url' => $site_url,
2183 ];
2184
2185 $args = array(
2186 'body' => wp_json_encode($post_data),
2187 'method' => 'POST',
2188 'timeout' => 20,
2189 'headers' => array(
2190 'Content-Type' => 'application/json',
2191 ),
2192 );
2193
2194 global $berq_log;
2195 $berq_log->info("Trigger addon sync");
2196
2197 $response = wp_remote_post('https://berqwp.com/wp-json/berqwp/active-addons', $args);
2198 $response_body = wp_remote_retrieve_body($response);
2199 $json = json_decode($response_body, true);
2200
2201 if (empty($json) || $json['status'] !== 'success') {
2202 return false;
2203 }
2204
2205 if (is_array($json['active_addons'])) {
2206
2207 $enable_fluid_images = in_array('fluid_images', $json['active_addons']);
2208 if ($enable_fluid_images && !get_option('berqwp_can_use_fluid_images')) {
2209 update_option('berqwp_can_use_fluid_images', 1);
2210 update_option('bwp_require_flush_cache', 1);
2211 } elseif (!$enable_fluid_images) {
2212 update_option('berqwp_can_use_fluid_images', 0);
2213 }
2214 }
2215 }
2216
2217 function berqwp_validate_url_array($urls)
2218 {
2219 $home_host = parse_url(home_url(), PHP_URL_HOST);
2220 $home_scheme = parse_url(home_url(), PHP_URL_SCHEME);
2221
2222 $urls = array_filter($urls, function ($url) use ($home_host) {
2223 $url_host = parse_url($url, PHP_URL_HOST);
2224
2225 // Relative URLs → treat as same host
2226 if (empty($url_host)) {
2227 return true;
2228 }
2229
2230 return $url_host === $home_host;
2231 });
2232
2233 $urls = array_map(function ($url) use ($home_scheme) {
2234 // Skip invalid URLs
2235 if (!is_string($url) || $url === '') {
2236 return $url;
2237 }
2238
2239 $parts = parse_url($url);
2240
2241 // Relative URL → leave as-is
2242 if (empty($parts['scheme'])) {
2243 return $url;
2244 }
2245
2246 // Rebuild URL with home scheme
2247 $parts['scheme'] = $home_scheme;
2248
2249 $new_url = $parts['scheme'] . '://';
2250 $new_url .= $parts['host'] ?? '';
2251 $new_url .= isset($parts['port']) ? ':' . $parts['port'] : '';
2252 $new_url .= $parts['path'] ?? '';
2253 $new_url .= isset($parts['query']) ? '?' . $parts['query'] : '';
2254 $new_url .= isset($parts['fragment']) ? '#' . $parts['fragment'] : '';
2255
2256 return $new_url;
2257 }, $urls);
2258
2259 return $urls;
2260 }
2261
2262 function berqwp_render_toggle($name, $checked) {
2263 ?>
2264 <label class="berq-toggle">
2265 <input type="checkbox" name="<?php echo esc_attr($name); ?>" <?php checked(1, $checked, true); ?>>
2266 <div class="berq-toggle-switch"></div>
2267 </label>
2268 <?php
2269 }
2270
2271 function bwp_log_recently_optimized_page($page_url)
2272 {
2273 if (empty($page_url)) return;
2274
2275 // berqwp_stored_page_cache passes a slug/path, not a full URL — reconstruct it
2276 if (!str_starts_with($page_url, 'http')) {
2277 $page_url = home_url($page_url);
2278 }
2279 $page_url = urldecode($page_url);
2280
2281 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2282 if (!is_array($log)) $log = [];
2283
2284 // Remove existing entry for this URL so re-caching moves it to the top
2285 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2286
2287 array_unshift($log, [
2288 'url' => esc_url_raw($page_url),
2289 'cached_at' => current_time('Y-m-d H:i:s'),
2290 ]);
2291
2292 if (count($log) > 200) {
2293 $log = array_slice($log, 0, 200);
2294 }
2295
2296 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2297 }
2298
2299 function bwp_clear_recently_optimized_log()
2300 {
2301 delete_option('berqwp_recently_optimized');
2302 }
2303
2304 function bwp_remove_recently_optimized_entry($page_url)
2305 {
2306 if (empty($page_url)) return;
2307
2308 $log = json_decode(get_option('berqwp_recently_optimized', '[]'), true);
2309 if (!is_array($log)) return;
2310
2311 $log = array_values(array_filter($log, fn($e) => $e['url'] !== $page_url));
2312 update_option('berqwp_recently_optimized', wp_json_encode($log), false);
2313 }
2314
2315 function berqwp_can_use_cloud() {
2316 $berqconfigs = berqConfigs::getInstance();
2317 $configs = $berqconfigs->get_configs();
2318 $license_key = berqwp_get_license_key();
2319
2320 return !empty($configs['site_id']) && !empty($configs['secret']) && !empty($configs['optimization_method']) && $configs['optimization_method'] == 'cloud' && !empty($license_key);
2321 }
2322