PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 1.9.7
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v1.9.7
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 / photon / class-berqBufferOptimize.php

class-berqBufferOptimize.php in BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript 1.9.7, at inc/photon/class-berqBufferOptimize.php

737 lines 26.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit;
3
4 class berqBufferOptimize {
5 public $external_js_excluded_keywords = [
6 'googleadservices',
7 'pagead2.googlesyndication.com',
8 'mediavine',
9 'googletagmanager.com/gtag',
10 'google.com/recaptcha',
11 'platform.illow.io/banner.js',
12 'code.etracker.com',
13 'cdn.trustindex.io/loader.js',
14 'static.getclicky.com',
15 'form.jotform.com',
16 'bunny.net',
17 '.exactdn.com/',
18 'maps.googleapis.com',
19 'js.stripe.com',
20 '.pressablecdn.com',
21 '.cdn77.net',
22 '.cloudflare.com',
23 '.fastly.net',
24 '.gcorelabs.com',
25 '.googleapis.com',
26 '.cloudfront.net',
27 '.b-cdn.net',
28 '.cachefly.net',
29 '.imagekit.io',
30 '.kxcdn.com',
31 '.stackpathcdn.com',
32 '.vo.msecnd.net',
33 '.akamaihd.net',
34 '.incapsula.com',
35 '.sucuricdn.net',
36 '.incapsula.com',
37 '.cdn.meta.net',
38 '.jet-stream.com',
39 '.cachefly.net',
40 '.cdnetworks.com',
41 'amazon-adsystem.com',
42 'adsbygoogle',
43 'google-analytics.com',
44 'maps.google.com',
45 'recaptcha',
46 'js.hsforms.net',
47 'hbspt.forms.create',
48 'ad-manager.min.js',
49 'fast.wistia.com',
50 ];
51
52 public $css_excluded_keywords = [
53 'bunny.net',
54 '.exactdn.com/',
55 'fonts.googleapis.com/css',
56 '.pressablecdn.com',
57 '.cdn77.net',
58 '.cloudflare.com',
59 '.fastly.net',
60 '.gcorelabs.com',
61 '.googleapis.com',
62 '.cloudfront.net',
63 '.b-cdn.net',
64 '.cachefly.net',
65 '.imagekit.io',
66 '.kxcdn.com',
67 '.stackpathcdn.com',
68 '.vo.msecnd.net',
69 '.akamaihd.net',
70 '.incapsula.com',
71 '.sucuricdn.net',
72 '.incapsula.com',
73 '.cdn.meta.net',
74 '.jet-stream.com',
75 '.cachefly.net',
76 '.cdnetworks.com',
77 ];
78
79 public $img_excluded_keywords = [
80 'i0.wp.com',
81 '.exactdn.com/',
82 '.smushcdn.com',
83 '.b-cdn.net',
84 '.pressablecdn.com',
85 '.cdn77.net',
86 '.cloudflare.com',
87 '.fastly.net',
88 '.gcorelabs.com',
89 '.googleapis.com',
90 '.cloudfront.net',
91 '.b-cdn.net',
92 '.cachefly.net',
93 '.imagekit.io',
94 '.kxcdn.com',
95 '.stackpathcdn.com',
96 '.vo.msecnd.net',
97 '.akamaihd.net',
98 '.incapsula.com',
99 '.sucuricdn.net',
100 '.incapsula.com',
101 '.cdn.meta.net',
102 '.jet-stream.com',
103 '.cachefly.net',
104 '.cdnetworks.com',
105 ];
106
107 public $skipOnLoadJS = [
108 'googletagmanager.com/gtag',
109 'cdn-cookieyes.com/client_data',
110 'static.getclicky.com',
111 'clarity.ms/',
112 'google.com',
113 'doubleclick.net',
114 'stats.wp.com',
115 '/elementor/',
116 '/elementor-pro',
117 'sp-scripts.min.js'
118 ];
119
120 public $optimization_mode = null;
121 public $js_css_exclude_urls = null;
122 public $use_cdn = false;
123 public $buffer = null;
124 public $image_lazy_loading = null;
125 public $lazy_load_youtube = null;
126 public $js_mode = null;
127
128 function __construct() {
129
130 $optimization_mode = get_option('berq_opt_mode');
131
132 if ($optimization_mode == 4) {
133 $optimization_mode = 'aggressive';
134 } elseif ($optimization_mode == 3) {
135 $optimization_mode = 'blaze';
136 } elseif ($optimization_mode == 2) {
137 $optimization_mode = 'medium';
138 } elseif ($optimization_mode == 1) {
139 $optimization_mode = 'basic';
140 }
141
142 $this->optimization_mode = $optimization_mode;
143 $this->js_css_exclude_urls = get_option('berq_exclude_js_css');
144 $this->image_lazy_loading = get_option('berqwp_image_lazyloading');
145 $this->lazy_load_youtube = get_option('berqwp_lazyload_youtube_embed');
146 $this->js_mode = get_option('berqwp_javascript_execution_mode');
147 // $this->use_cdn = get_option('berqwp_enable_cdn');
148
149 if (berq_is_localhost()) {
150 $this->use_cdn = null;
151 }
152
153 }
154
155 function optimize_buffer($buffer, $page_slug) {
156
157 // if (!berq_is_localhost()) {
158 // $berqCriticalCSS = new berqCriticalCSS();
159 // $criticalcss = $berqCriticalCSS->get_css($page_slug);
160
161 // if (!empty($criticalcss)) {
162 // $buffer = $this->css_optimize($buffer, true, false);
163 // $buffer = $berqCriticalCSS->add_css_to_buffer($buffer, $criticalcss);
164 // } else {
165 // $buffer = $this->css_optimize($buffer);
166 // }
167 // }
168
169 $buffer = $this->lazyload_iframes($buffer);
170 $buffer = $this->js_optimize($buffer);
171 $buffer = $this->optimize_images($buffer);
172
173 // global $berqCDN;
174 // $buffer = $berqCDN->finish_queue($buffer, $page_slug);
175
176 $script = "
177 <script data-berqwp defer>
178 var comment = document.createComment(' This website is optimized using the BerqWP plugin. @".time()." ');
179 document.documentElement.insertBefore(comment, document.documentElement.firstChild);
180
181 function isMobileDevice() {
182 return /Mobi|Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent);
183 }
184
185 function astraHeaderClass() {
186 if (isMobileDevice() && window.screen.width <= 999) {
187 if (document.body.classList.contains('theme-astra') && !document.body.classList.contains('ast-header-break-point')) {
188 document.body.classList.add('ast-header-break-point');
189 }
190 }
191 }
192
193 astraHeaderClass();
194 window.addEventListener('resize', function() {
195 astraHeaderClass();
196 });
197
198 </script>
199
200 <script data-berqwp>
201 async function berqwp_add_assets_browser_cache(asset_urls) {
202 if (!('caches' in window)) {
203 console.error('Cache API is not supported in this browser.');
204 return;
205 }
206
207 const fetchPromises = [];
208
209 asset_urls.forEach(url => {
210 if (url) {
211 fetchPromises.push(
212 fetch(url, {
213 method: 'GET',
214 mode: 'cors' // Ensure CORS mode is set
215 })
216 .then(response => {
217 if (!response.ok) {
218 console.error('Failed to fetch:', response);
219 }
220 return response;
221 })
222 .catch(error => {
223 console.error('Fetch error:', error);
224 return null;
225 })
226 );
227 }
228 });
229
230 try {
231 const responses = await Promise.all(fetchPromises);
232
233 // Open or create a cache
234 const cache = await caches.open('berqwp-assets-cache');
235
236 responses.forEach(response => {
237 if (response && response.ok) {
238 console.log('Preloading and caching:', response.url);
239
240 // Add the response to the cache
241 cache.put(response.url, response.clone());
242 }
243 });
244 } catch (error) {
245 console.error('Error during fetching and caching:', error);
246 }
247 }
248
249 // async function berqwp_add_assets_browser_cache(asset_urls) {
250 // const fetchPromises = [];
251
252 // asset_urls.forEach(url => {
253 // if (url) {
254 // fetchPromises.push(
255 // fetch(url, {
256 // method: 'GET',
257 // mode: 'cors' // Ensure CORS mode is set
258 // })
259 // .then(response => {
260 // if (!response.ok) {
261 // // If response is not successful
262 // console.error(response);
263 // // Handle the failed response here if needed
264 // }
265 // return response; // Always return the response, successful or not
266 // })
267 // .catch(error => {
268 // // Handle any errors that occur during the fetch
269 // console.error(error);
270 // return null; // Return null to indicate that the fetch failed
271 // })
272 // );
273 // }
274 // });
275
276 // try {
277 // // Execute all fetch promises in parallel and wait for all of them to complete
278 // const responses = await Promise.all(fetchPromises);
279
280 // // Handle responses here
281 // responses.forEach(response => {
282 // if (response && response.ok) {
283 // // If response is successful (status code 200-299)
284 // console.log('Preloaded: '+response.url);
285 // // Process the successful response here
286 // }
287 // });
288 // } catch (error) {
289 // // Handle errors that occurred during the fetch
290 // console.error('Error during fetch:', error);
291 // }
292 // }
293
294 </script>
295
296 <script data-berqwp id='prefetch-links' defer>
297 // Set to keep track of prefetched links
298 const prefetchedLinks = new Set();
299
300 // Get all anchor tags on the page
301 const links = document.querySelectorAll('a');
302
303 // Loop through each anchor tag
304 links.forEach(link => {
305 // Add mouseover event listener
306 link.addEventListener('mouseover', () => {
307 // Check if the link has already been prefetched
308 if (!prefetchedLinks.has(link.href)) {
309 // Create a link element
310 const prefetchLink = document.createElement('link');
311 prefetchLink.rel = 'prefetch';
312 prefetchLink.href = link.href;
313
314 // Append the link element to the head of the document
315 document.head.appendChild(prefetchLink);
316
317 // Add the link to the set of prefetched links
318 prefetchedLinks.add(link.href);
319 }
320 });
321 });
322
323 </script>
324 ";
325
326 $preload = '';
327 $preload .= "
328 <script data-berqwp async>
329 Object.defineProperty(document, 'readyState', {
330 get: () => 'interactive',
331 set: () => {}
332 });
333 </script>
334 ";
335 if ($this->image_lazy_loading) {
336 $preload .= "
337 <script data-berqwp id='berqwp-img-lazyload' async>
338 document.addEventListener('DOMContentLoaded', function () {
339 var berq_img_lazy_options = {
340 root: null, // null means the viewport
341 rootMargin: '200px',
342 threshold: 0
343 };
344
345 var img_observer = new IntersectionObserver(function (entries, observer) {
346 entries.forEach(function (entry) {
347 if (entry.isIntersecting) {
348 let img = entry.target;
349 let imgSrc = img.getAttribute('data-src');
350 let imgSrcset = img.getAttribute('data-berqwp-srcset');
351
352 // Set the actual image source from data-src
353 if (imgSrc !== null) {
354 img.src = imgSrc;
355 }
356
357 if (imgSrcset !== null) {
358 img.srcset = imgSrcset;
359 }
360
361 if (img.getAttribute('data-srcset') !== null) {
362 img.srcset = img.getAttribute('data-srcset');
363 }
364
365 // You might want to remove the data-src attribute after loading
366 img.removeAttribute('data-src');
367 img.removeAttribute('data-berqwp-srcset');
368
369 img_observer.unobserve(img);
370 }
371 });
372 }, berq_img_lazy_options);
373
374 function berqwp_lazyload_images() {
375 let lazyImages = document.querySelectorAll('img[data-src]');
376 lazy_img_int = 1000;
377
378 lazyImages.forEach(function (img) {
379 img_observer.observe(img);
380 });
381 }
382
383 berqwp_lazyload_images();
384 setInterval(berqwp_lazyload_images, 1000);
385
386 });
387
388 </script>
389
390 <script data-berqwp >
391
392 // Get the style tag element
393 const styleTag = document.getElementById('berqwp-critical-css');
394
395 if (styleTag) {
396 // Extract the CSS content from the style tag
397 const cssContent = styleTag.textContent;
398
399 // Regular expression to match CSS rules
400 const ruleRegex = /([^{}]+)\{([^{}]+)\}/g;
401
402 // Regular expression to match background or background-image properties with url() function
403 const backgroundRegex = /(?:background(?:-image)?:[^;]*?url\([^)]*\))/g;
404
405 // Array to store the extracted CSS properties
406 var extractedProperties = [];
407
408 // Match CSS rules in the CSS content
409 let ruleMatch;
410 while ((ruleMatch = ruleRegex.exec(cssContent)) !== null) {
411 const cssBlockSelector = ruleMatch[1].trim();
412 const cssProperties = ruleMatch[2];
413
414 // Match background properties in the CSS properties
415 let backgroundMatch;
416 while ((backgroundMatch = backgroundRegex.exec(cssProperties)) !== null) {
417 let prop_value = backgroundMatch[0].trim().replace('background:', 'background-image:');
418
419 extractedProperties.push({ cssBlockSelector, backgroundValue: prop_value });
420 /* extractedProperties.push({ cssBlockSelector, backgroundValue: backgroundMatch[0].trim() }); */
421 }
422 }
423
424 // console.log(extractedProperties);
425
426 // Iterate through the extracted properties and modify background images directly in the style tag
427 let modifiedCssContent = cssContent;
428 extractedProperties.forEach(property => {
429
430 // Replace the original background value with 'none'
431 let bg_img_hash = btoa(property.backgroundValue);
432 const regex = /url\([^)]*\)/;
433 // const regex = /url\(([^)]*)\)/; // fix bg image error
434
435
436 // Replace the URL function with the new URL
437 /* const prop_value = property.backgroundValue.replace(regex, 'urlbgberq(#'+bg_img_hash+')'); */
438 // const prop_value = property.backgroundValue.replace(regex, 'url(data:image/svg+xml;base64,'+bg_img_hash+')');
439 const prop_value = property.backgroundValue.replace(regex, 'unset');
440 // let lcp_exists = lcp_ele.some(element => property.backgroundValue.includes(element));
441 let lcp_exists = false;
442
443 if (lcp_exists) {
444 modifiedCssContent = modifiedCssContent.replace(property.backgroundValue, property.backgroundValue+' !important;');
445 // modifiedCssContent = modifiedCssContent.replace(property.backgroundValue, prop_value);
446 } else {
447 modifiedCssContent = modifiedCssContent.replace(property.backgroundValue, prop_value);
448
449 }
450
451 });
452
453 // Update the text content of the style tag with the modified CSS content
454 styleTag.textContent = modifiedCssContent;
455 }
456
457
458 </script>
459 ";
460 }
461
462 if ($this->lazy_load_youtube == 1) {
463
464 $script .= "
465 <script data-berqwp defer>
466 // document.addEventListener('DOMContentLoaded', function () {
467 var options = {
468 root: null, // null means the viewport
469 rootMargin: '0px', // adjust as needed
470 threshold: 0.1 // adjust as needed
471 };
472
473 var observer = new IntersectionObserver(function (entries, observer) {
474 entries.forEach(function (entry) {
475 if (entry.isIntersecting) {
476 let item = entry.target;
477 let iframe = item.getAttribute('data-embed');
478
479 let wrapper = document.createElement('div');
480 wrapper.innerHTML = iframe;
481 let iframeElement = wrapper.firstChild;
482
483 // Insert the iframe next to the item
484 item.insertAdjacentElement('afterend', iframeElement);
485
486 // Remove the original item
487 item.remove();
488
489 observer.unobserve(item);
490 }
491 });
492 }, options);
493
494 let yt_em = document.querySelectorAll('.berqwp-lazy-youtube');
495 yt_em.forEach(function (item) {
496 observer.observe(item);
497 });
498 // });
499 </script>
500 ";
501 }
502
503 $afterHead = apply_filters( 'berqwp_buffer_after_head_open', '' );
504 $buffer = preg_replace('/<head(\s[^>]*)?>/i', '<head$1>' . $afterHead, $buffer);
505
506 $beforeHeadClosing = apply_filters( 'berqwp_buffer_before_closing_head', $preload );
507 $buffer = str_replace('</head>', $beforeHeadClosing . '</head>', $buffer);
508
509 $beforeBodyClose = apply_filters( 'berqwp_buffer_before_closing_body', $script );
510 $buffer = str_replace('</body>', $beforeBodyClose . '</body>', $buffer);
511
512 $this->buffer = $buffer;
513
514 add_filter( 'berqwp_cache_buffer', [$this, 'update_buffer'] );
515 }
516
517 function lazyload_iframes($buffer) {
518 // Lazyload YouTube embeds
519 if ($this->lazy_load_youtube == 1) {
520 $buffer = preg_replace_callback(
521 '/<iframe(.*?)<\/iframe>/s',
522 function ($matches) {
523 $attrs = str_replace('"', '\'', $matches[1]);
524 return '<div class="berqwp-lazy-youtube" data-embed="<iframe' . $attrs . '</iframe>"></div>';
525 },
526 $buffer
527 );
528 }
529 return $buffer;
530 }
531
532 function update_buffer($buffer) {
533 return $this->buffer;
534 }
535
536 function css_optimize($buffer, $disableCDN = null, $forceDefault = true) {
537
538 $currentCDNstat = $this->use_cdn;
539
540 if ($disableCDN) {
541 $this->use_cdn = false;
542 }
543
544 // CSS optimization
545 $styleOptimizer = new berqStyleOptimizer();
546
547 if ($forceDefault) {
548 $styleOptimizer->set_loading('default');
549 } else {
550 if ($this->optimization_mode == 'blaze' || $this->optimization_mode == 'medium') {
551 $styleOptimizer->set_loading('preload');
552 }
553
554 if ($this->optimization_mode == 'basic') {
555 $styleOptimizer->set_loading('default');
556 }
557 }
558
559
560 $buffer = $styleOptimizer->run_optimization($this, $buffer);
561 $this->use_cdn = $currentCDNstat;
562
563 return $buffer;
564 }
565
566 function js_optimize($buffer) {
567 // JavaScript optimization
568 $scriptOptimizer = new berqScriptOptimizer();
569
570 if ($this->optimization_mode == 'medium') {
571 $scriptOptimizer->set_loading('preload');
572 }
573
574 if ($this->optimization_mode == 'basic') {
575 $scriptOptimizer->set_loading('default');
576 }
577
578 $buffer = apply_filters( 'berqwp_before_script_optimization', $buffer );
579 $buffer = $scriptOptimizer->run_optimization($this, $buffer);
580
581 return $buffer;
582 }
583
584 function optimize_images($buffer) {
585 if ($this->image_lazy_loading) {
586 $berqImages = new berqImagesOpt();
587 $berqImages->image_lazy_loading = $this->image_lazy_loading;
588 $buffer = $berqImages->optimize_images($buffer);
589 }
590 return $buffer;
591 }
592
593 public static function parallelCurlRequests($urls, $postDataArray, $requestMethod = 'POST', $contentType = 'application/x-www-form-urlencoded', $userAgent = 'BerqWP Bot') {
594 $curlHandles = [];
595 $result = [];
596 $requestCounter = 0; // Initialize the counter
597 $maxRequestsBeforeSleep = 30;
598
599 // Create cURL handles for each URL
600 foreach ($urls as $index => $url) {
601 $ch = curl_init($url);
602
603 // Set cURL options
604 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
605 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $requestMethod);
606
607 if ($requestMethod == 'POST') {
608 curl_setopt($ch, CURLOPT_POSTFIELDS, $postDataArray[$index]);
609 }
610
611 curl_setopt($ch, CURLOPT_HTTPHEADER, [
612 'Content-Type: ' . $contentType,
613 'User-Agent: ' . $userAgent,
614 ]);
615
616 // curl_setopt($ch, CURLOPT_TIMEOUT, 30);
617 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
618 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
619
620 // Add cURL handle to the array
621 $curlHandles[$index] = $ch;
622 }
623
624 // Initialize multi cURL handler
625 $multiHandle = curl_multi_init();
626
627 // Add cURL handles to the multi cURL handler
628 foreach ($curlHandles as $ch) {
629 curl_multi_add_handle($multiHandle, $ch);
630 }
631
632 // Execute all cURL requests simultaneously
633 do {
634 curl_multi_exec($multiHandle, $running);
635
636 $requestCounter++; // Increment the counter for each request
637 if ($requestCounter >= $maxRequestsBeforeSleep) {
638 usleep(2000000); // Sleep for 2 seconds (2000000 microseconds)
639 $requestCounter = 0; // Reset the counter
640 }
641
642 } while ($running > 0);
643
644 // Retrieve the results from each cURL handle
645 foreach ($curlHandles as $index => $ch) {
646 $result[$index] = curl_multi_getcontent($ch);
647 curl_multi_remove_handle($multiHandle, $ch);
648 }
649
650 // Close the multi cURL handler
651 curl_multi_close($multiHandle);
652
653 return $result;
654 }
655
656 function optimize_external_js($tag)
657 {
658 $src = $this->get_src_from_script($tag);
659
660 if ($src !== false) {
661 if (strpos($src, 'http') === 0 || strpos($src, '//') === 0) {
662 $parsed_url = parse_url($src);
663 if (isset ($parsed_url['host']) && $parsed_url['host'] !== $this->domain) {
664 $kw_found = false;
665
666 foreach ($this->external_js_excluded_keywords as $keyword) {
667 if (stripos($src, $keyword) !== false) {
668 $kw_found = true;
669 }
670 }
671
672 if (!$kw_found) {
673 $cache = $this->cache_scripts([$src]);
674 $json_data = json_decode($cache);
675
676 if ($json_data !== null) {
677 if (isset ($json_data->status) && $json_data->status == 'success') {
678
679 // var_dump(md5($src));
680 // var_dump($json_data->urls->{md5($src)});
681 // exit;
682 $newSRC = $json_data->urls->{md5($src)};
683
684
685 // Create a Simple HTML DOM object
686 $html = str_get_html($tag);
687
688 // Find all script tags
689 foreach ($html->find('script') as $scriptTag) {
690 $scriptTag->src = $newSRC;
691 }
692
693 $tag = $html->save();
694
695 // Clear Simple HTML DOM object
696 $html->clear();
697 unset($html);
698
699 }
700 }
701
702 }
703
704 }
705
706
707 }
708 }
709
710 return $tag;
711 }
712
713 function get_src_from_script($html)
714 {
715 if (empty($html)) {
716 return false;
717 }
718
719 // Create a Simple HTML DOM object
720 $html = str_get_html($html);
721
722 // Find all script tags
723 foreach ($html->find('script') as $scriptTag) {
724 $src = $scriptTag->src;
725 }
726
727 // Clear Simple HTML DOM object
728 $html->clear();
729 unset($html);
730
731 if (empty($src)) {
732 return false;
733 }
734
735 return $src;
736 }
737 }