PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / V3.3.2
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder vV3.3.2
V3.3.2 3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 All 139 releases
bit-form / includes / Core / Util / CacheCompat.php

CacheCompat.php in Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder V3.3.2, at includes/Core/Util/CacheCompat.php

867 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Compatibility layer for caching / optimization plugins.
5 *
6 * The frontend needs the inline config (window.bf_globals) and the generated
7 * runtime bundle to execute in order. Optimizers that minify, combine, defer or
8 * delay JavaScript can reorder or withhold either one. Registers Bit Form's
9 * script patterns with each optimizer's exclusion filter, and stamps generic
10 * no-optimize attributes as a fallback for those that honor them.
11 *
12 * Stylesheets need the same protection for a different reason: a form's styles
13 * are atomic single-property classes generated per form, so a combiner that
14 * reorders them against theme CSS, or an unused-CSS pass that drops the classes
15 * it cannot see in the markup, leaves the form unstyled.
16 *
17 * @since 3.2.2
18 */
19
20 namespace BitCode\BitForm\Core\Util;
21
22 use BitCode\BitForm\Core\Database\FormModel;
23
24 if (!defined('ABSPATH')) {
25 exit;
26 }
27
28 final class CacheCompat
29 {
30 /** True once a purge is queued for this request. */
31 private static $purgeScheduled = false;
32
33 /** Post IDs queued for purging. */
34 private static $purgePostIds = [];
35
36 /**
37 * Frontend script handle prefixes. bit-form-all-script covers both the free
38 * runtime handle (bit-form-all-script-test) and the pro data-view handle.
39 */
40 private const HANDLE_PREFIXES = [
41 'bit-form-all-script',
42 'bitform-bf-globals-',
43 'bitform-show-picker-bridge',
44 'bitforms_recaptcha',
45 ];
46
47 /**
48 * Frontend style handle prefixes. Covers the generated per-form sheets
49 * (bitform-style-15, bitform-style-15-formid, bitform-style-custom-15), the
50 * Pro data views (bitform-style-view-3), the page builder widgets and the
51 * standalone, conversational and preview views.
52 */
53 private const STYLE_HANDLE_PREFIXES = [
54 'bf-inline-style',
55 'bitform-bricks-',
56 'bitform-conversational-',
57 'bitform-elementor-style',
58 'bitform-entry-edit-',
59 'bitform-preview-',
60 'bitform-standalone-',
61 'bitform-style',
62 ];
63
64 /** Attributes the optimizers that read markup instead of filters honor. */
65 private const NO_OPTIMIZE_ATTRIBUTES = 'data-no-optimize="1" data-no-defer="1" data-no-minify="1" data-cfasync="false" data-jetpack-boost="ignore" nowprocket nitro-exclude ';
66
67 public static function register()
68 {
69 // WP Rocket
70 add_filter('rocket_delay_js_exclusions', [self::class, 'appendExclusionsArray']);
71 add_filter('rocket_exclude_defer_js', [self::class, 'appendExclusionsArray']);
72 add_filter('rocket_exclude_js', [self::class, 'appendExclusionsArray']);
73 add_filter('rocket_minify_excluded_external_js', [self::class, 'appendExclusionsArray']);
74 add_filter('rocket_excluded_inline_js_content', [self::class, 'appendExclusionsArray']);
75
76 // LiteSpeed Cache
77 add_filter('litespeed_optimize_js_excludes', [self::class, 'appendExclusionsArray']);
78 add_filter('litespeed_optm_js_defer_exc', [self::class, 'appendExclusionsArray']);
79
80 // Perfmatters
81 add_filter('perfmatters_delay_js_exclusions', [self::class, 'appendExclusionsArray']);
82 add_filter('perfmatters_defer_js_exclusions', [self::class, 'appendExclusionsArray']);
83
84 // Autoptimize (comma-separated string)
85 add_filter('autoptimize_filter_js_exclude', [self::class, 'appendExclusionsString']);
86
87 // SiteGround Optimizer (script handles)
88 add_filter('sgo_js_minify_exclude', [self::class, 'appendHandles']);
89 add_filter('sgo_javascript_combine_exclude', [self::class, 'appendHandles']);
90 add_filter('sgo_js_async_exclude', [self::class, 'appendHandles']);
91
92 // FlyingPress
93 add_filter('flying_press_exclude_from_delay:js', [self::class, 'appendExclusionsArray']);
94 add_filter('flying_press_exclude_from_defer:js', [self::class, 'appendExclusionsArray']);
95 add_filter('flying_press_exclude_from_minify:js', [self::class, 'appendExclusionsArray']);
96
97 // WP Optimize
98 add_filter('wp-optimize-minify-default-exclusions', [self::class, 'appendExclusionsArray']);
99
100 // W3 Total Cache (matches the tag and file path; this filter has no handle)
101 add_filter('w3tc_minify_js_do_tag_minification', [self::class, 'denyTagMinification'], 10, 3);
102
103 // Hummingbird, at 20 so it runs after Hummingbird's own settings filters at 10.
104 // These four carry styles as well as scripts, so they test both handle lists.
105 add_filter('wphb_minify_resource', [self::class, 'denyForBitformAsset'], 20, 2);
106 add_filter('wphb_combine_resource', [self::class, 'denyForBitformAsset'], 20, 2);
107 add_filter('wphb_defer_resource', [self::class, 'denyForBitformAsset'], 20, 2);
108 add_filter('wphb_inline_resource', [self::class, 'denyForBitformAsset'], 20, 2);
109
110 // Jetpack Boost, Page Optimize and WordPress.com asset concatenation
111 add_filter('js_do_concat', [self::class, 'denyForBitformHandle'], 20, 2);
112 add_filter('css_do_concat', [self::class, 'denyForBitformStyleHandle'], 20, 2);
113
114 self::registerStyleFilters();
115
116 // Generic no-optimize attributes on our own script tags
117 add_filter('script_loader_tag', [self::class, 'addNoOptimizeAttributes'], 10, 2);
118 add_filter('wp_inline_script_attributes', [self::class, 'addInlineNoOptimizeAttributes'], 10, 2);
119 add_filter('style_loader_tag', [self::class, 'addStyleNoOptimizeAttributes'], 10, 2);
120 }
121
122 /** Stylesheet counterparts of the script exclusion filters above. */
123 private static function registerStyleFilters()
124 {
125 // WP Rocket, including the Remove Unused CSS safelist
126 add_filter('rocket_exclude_css', [self::class, 'appendStyleExclusionsArray']);
127 add_filter('rocket_exclude_defer_css', [self::class, 'appendStyleExclusionsArray']);
128 add_filter('rocket_rucss_safelist', [self::class, 'appendStyleExclusionsArray']);
129
130 // LiteSpeed Cache, including the Unique CSS pass
131 add_filter('litespeed_optimize_css_excludes', [self::class, 'appendStyleExclusionsArray']);
132 add_filter('litespeed_optm_css_defer_exc', [self::class, 'appendStyleExclusionsArray']);
133 add_filter('litespeed_optm_ucss_exc', [self::class, 'appendStyleExclusionsArray']);
134
135 // Perfmatters Used CSS
136 add_filter('perfmatters_used_css_excluded_stylesheets', [self::class, 'appendStyleExclusionsArray']);
137
138 // Autoptimize (comma-separated string)
139 add_filter('autoptimize_filter_css_exclude', [self::class, 'appendStyleExclusionsString']);
140
141 // SiteGround Optimizer (style handles)
142 add_filter('sgo_css_minify_exclude', [self::class, 'appendStyleHandles']);
143 add_filter('sgo_css_combine_exclude', [self::class, 'appendStyleHandles']);
144
145 // FlyingPress
146 add_filter('flying_press_exclude_from_minify:css', [self::class, 'appendStyleExclusionsArray']);
147 add_filter('flying_press_exclude_from_used_css', [self::class, 'appendStyleExclusionsArray']);
148
149 // W3 Total Cache (matches the tag and file path; this filter has no handle)
150 add_filter('w3tc_minify_css_do_tag_minification', [self::class, 'denyStyleTagMinification'], 10, 3);
151 }
152
153 /** Register the cache purge that runs after a form changes. */
154 public static function registerPurgeHooks()
155 {
156 add_action('bitform_admin_form_changed', [self::class, 'schedulePurge']);
157 }
158
159 /**
160 * Queue one cache purge for the end of the request.
161 *
162 * Collects the page list now and runs the purge on shutdown. Disabled by the
163 * bitform_purge_caches_on_form_change filter.
164 */
165 public static function schedulePurge()
166 {
167 if (self::$purgeScheduled) {
168 return;
169 }
170 if (!apply_filters('bitform_purge_caches_on_form_change', true)) {
171 return;
172 }
173 self::$purgeScheduled = true;
174 self::$purgePostIds = self::collectFormPageIds();
175 add_action('shutdown', [self::class, 'purgeCaches'], 100);
176 }
177
178 /**
179 * Post IDs that have rendered a Bit Form.
180 *
181 * @return array
182 */
183 private static function collectFormPageIds()
184 {
185 $postIds = [];
186 try {
187 $forms = (new FormModel())->get(['generated_script_page_ids']);
188 if (is_wp_error($forms) || !is_array($forms)) {
189 return [];
190 }
191 foreach ($forms as $form) {
192 $pages = json_decode(isset($form->generated_script_page_ids) ? $form->generated_script_page_ids : '', true);
193 if (!is_array($pages)) {
194 continue;
195 }
196 foreach (array_keys($pages) as $postId) {
197 $postId = absint($postId);
198 if ($postId) {
199 $postIds[] = $postId;
200 }
201 }
202 }
203 } catch (\Throwable $err) {
204 Log::debug_log('Cache purge page lookup failed: ' . $err->getMessage());
205 return [];
206 }
207 return array_values(array_unique($postIds));
208 }
209
210 /**
211 * Purge the cached pages that render a Bit Form.
212 *
213 * Purges page by page, or flushes everything once the page count passes
214 * bitform_cache_purge_page_limit.
215 */
216 public static function purgeCaches()
217 {
218 $postIds = apply_filters('bitform_cache_purge_post_ids', self::$purgePostIds);
219 $postIds = is_array($postIds) ? array_values(array_filter(array_map('absint', $postIds))) : [];
220
221 // No page has rendered a form, so nothing cached can be stale.
222 if (empty($postIds)) {
223 do_action('bitform_purge_caches', [], 'none');
224 return;
225 }
226
227 $limit = (int) apply_filters('bitform_cache_purge_page_limit', 50);
228 if ($limit > 0 && count($postIds) > $limit) {
229 self::purgeEverything();
230 do_action('bitform_purge_caches', $postIds, 'everything');
231 return;
232 }
233
234 $urls = [];
235 foreach ($postIds as $postId) {
236 $url = get_permalink($postId);
237 if (is_string($url) && '' !== $url) {
238 $urls[] = $url;
239 }
240 }
241
242 self::purgePages($postIds, $urls);
243 self::purgeWholeCacheForPluginsWithoutPageApi();
244
245 // $mode is 'pages', 'everything' or 'none'.
246 do_action('bitform_purge_caches', $postIds, 'pages');
247 }
248
249 /**
250 * Per-page purge for every caching plugin that exposes one.
251 *
252 * @param array $postIds
253 * @param array $urls
254 *
255 * @return void
256 */
257 private static function purgePages($postIds, $urls)
258 {
259 // WP Rocket
260 self::tryPurge(function () use ($postIds) {
261 if (function_exists('rocket_clean_post')) {
262 foreach ($postIds as $postId) {
263 \rocket_clean_post($postId);
264 }
265 }
266 });
267
268 // LiteSpeed Cache
269 self::tryPurge(function () use ($postIds) {
270 if (has_action('litespeed_purge_post')) {
271 foreach ($postIds as $postId) {
272 do_action('litespeed_purge_post', $postId);
273 }
274 }
275 });
276
277 // W3 Total Cache
278 self::tryPurge(function () use ($postIds) {
279 if (function_exists('w3tc_flush_post')) {
280 foreach ($postIds as $postId) {
281 \w3tc_flush_post($postId);
282 }
283 }
284 });
285
286 // WP Super Cache
287 self::tryPurge(function () use ($urls) {
288 if (function_exists('wpsc_delete_url_cache')) {
289 foreach ($urls as $url) {
290 \wpsc_delete_url_cache($url);
291 }
292 }
293 });
294
295 // Cache Enabler
296 self::tryPurge(function () use ($postIds) {
297 if (class_exists('\Cache_Enabler')) {
298 foreach ($postIds as $postId) {
299 \Cache_Enabler::clear_page_cache_by_post_id($postId);
300 }
301 }
302 });
303
304 // WP-Optimize
305 self::tryPurge(function () use ($postIds) {
306 if (class_exists('\WPO_Page_Cache')) {
307 foreach ($postIds as $postId) {
308 \WPO_Page_Cache::delete_single_post_cache($postId);
309 }
310 }
311 });
312
313 // SiteGround Optimizer
314 self::tryPurge(function () use ($urls) {
315 if (function_exists('sg_cachepress_purge_cache')) {
316 foreach ($urls as $url) {
317 \sg_cachepress_purge_cache($url);
318 }
319 }
320 });
321
322 // NitroPack
323 self::tryPurge(function () use ($urls) {
324 if (function_exists('nitropack_purge_url')) {
325 foreach ($urls as $url) {
326 \nitropack_purge_url($url);
327 }
328 }
329 });
330
331 // Nginx Helper (server-level FastCGI / Redis page cache)
332 self::tryPurge(function () use ($urls) {
333 if (defined('NGINX_HELPER_BASENAME') || class_exists('Nginx_Helper')) {
334 foreach ($urls as $url) {
335 do_action('rt_nginx_helper_purge_url', $url);
336 }
337 }
338 });
339
340 // Elementor stores rendered document HTML in post meta with a 24h TTL.
341 self::tryPurge(function () use ($postIds) {
342 if (class_exists('\Elementor\Core\Base\Document')) {
343 foreach ($postIds as $postId) {
344 delete_post_meta($postId, \Elementor\Core\Base\Document::CACHE_META_KEY);
345 }
346 }
347 });
348 }
349
350 /**
351 * Full flush for caching plugins that expose no per-page purge API.
352 *
353 * Each call is a no-op unless that plugin is installed. Disabled by the
354 * bitform_cache_full_purge_fallback filter.
355 *
356 * @return void
357 */
358 private static function purgeWholeCacheForPluginsWithoutPageApi()
359 {
360 if (!apply_filters('bitform_cache_full_purge_fallback', true)) {
361 return;
362 }
363
364 // FlyingPress
365 self::tryPurge(function () {
366 if (class_exists('\FlyingPress\Purge')) {
367 \FlyingPress\Purge::purge_everything();
368 }
369 });
370
371 // Breeze
372 self::tryPurge(function () {
373 if (class_exists('\Breeze_PurgeCache')) {
374 \Breeze_PurgeCache::breeze_cache_flush();
375 }
376 });
377
378 // Hummingbird
379 self::tryPurge(function () {
380 if (has_action('wphb_clear_page_cache')) {
381 do_action('wphb_clear_page_cache');
382 }
383 });
384
385 // Swift Performance
386 self::tryPurge(function () {
387 if (class_exists('\Swift_Performance_Cache')) {
388 \Swift_Performance_Cache::clear_all_cache();
389 }
390 });
391
392 // Comet Cache
393 self::tryPurge(function () {
394 if (class_exists('\comet_cache')) {
395 \comet_cache::clear();
396 }
397 });
398
399 // WP Fastest Cache
400 self::tryPurge(function () {
401 if (class_exists('WpFastestCache')) {
402 do_action('wpfc_clear_all_cache', true);
403 }
404 });
405
406 // WP Engine
407 self::tryPurge(function () {
408 if (class_exists('\WpeCommon')) {
409 \WpeCommon::purge_varnish_cache();
410 }
411 });
412 }
413
414 /**
415 * Flush every installed caching plugin's whole cache.
416 *
417 * @return void
418 */
419 private static function purgeEverything()
420 {
421 self::tryPurge(function () {
422 if (function_exists('rocket_clean_domain')) {
423 \rocket_clean_domain();
424 }
425 });
426 self::tryPurge(function () {
427 if (has_action('litespeed_purge_all')) {
428 do_action('litespeed_purge_all');
429 }
430 });
431 self::tryPurge(function () {
432 if (function_exists('w3tc_flush_all')) {
433 \w3tc_flush_all();
434 }
435 });
436 self::tryPurge(function () {
437 if (function_exists('wp_cache_clear_cache')) {
438 is_multisite() ? \wp_cache_clear_cache(get_current_blog_id()) : \wp_cache_clear_cache();
439 }
440 });
441 self::tryPurge(function () {
442 if (class_exists('\Cache_Enabler')) {
443 \Cache_Enabler::clear_complete_cache();
444 }
445 });
446 self::tryPurge(function () {
447 if (class_exists('\WPO_Page_Cache')) {
448 \WPO_Page_Cache::instance()->purge();
449 }
450 });
451 self::tryPurge(function () {
452 if (function_exists('sg_cachepress_purge_everything')) {
453 \sg_cachepress_purge_everything();
454 }
455 });
456 self::tryPurge(function () {
457 if (function_exists('nitropack_purge')) {
458 \nitropack_purge();
459 }
460 });
461 self::tryPurge(function () {
462 if (defined('NGINX_HELPER_BASENAME') || class_exists('Nginx_Helper')) {
463 do_action('rt_nginx_helper_purge_all');
464 }
465 });
466 // Elementor's document cache, CSS files and asset data.
467 self::tryPurge(function () {
468 if (class_exists('\Elementor\Plugin')) {
469 \Elementor\Plugin::$instance->files_manager->clear_cache();
470 }
471 });
472
473 self::purgeWholeCacheForPluginsWithoutPageApi();
474 }
475
476 /**
477 * Run one purge, swallowing third-party failures.
478 *
479 * @param callable $purge
480 *
481 * @return void
482 */
483 private static function tryPurge($purge)
484 {
485 try {
486 $purge();
487 } catch (\Throwable $err) {
488 Log::debug_log('Cache purge failed: ' . $err->getMessage());
489 }
490 }
491
492 /**
493 * Substrings optimizers match against a script's URL, handle or inline body.
494 *
495 * @return array
496 */
497 public static function exclusionPatterns()
498 {
499 $patterns = [
500 'bf_globals', // inline config content
501 'bitform', // handles + generated file names (bitform-js-*, bitforms_*)
502 'bitforms/form-scripts', // uploads path of the generated runtime bundle
503 'bit-form-all-script', // runtime bundle handle
504 ];
505 $filtered = apply_filters('bitform_cache_exclusion_patterns', $patterns);
506 return is_array($filtered) ? $filtered : $patterns;
507 }
508
509 /**
510 * Substrings optimizers match against a stylesheet's URL or tag.
511 *
512 * @return array
513 */
514 public static function styleExclusionPatterns()
515 {
516 $patterns = [
517 'bitform', // handles + generated file names (bitform-15-formid.css)
518 'bitforms/form-styles', // uploads path of the generated stylesheets
519 ];
520 $filtered = apply_filters('bitform_cache_style_exclusion_patterns', $patterns);
521 return is_array($filtered) ? $filtered : $patterns;
522 }
523
524 /**
525 * Append script patterns to an array-based optimizer filter. Typed loosely
526 * because optimizers pass mixed shapes through these filters.
527 *
528 * @param mixed $exclusions
529 *
530 * @return mixed
531 */
532 public static function appendExclusionsArray($exclusions)
533 {
534 return self::mergePatternsIntoArray($exclusions, self::exclusionPatterns());
535 }
536
537 /**
538 * Append style patterns to an array-based optimizer filter.
539 *
540 * @param mixed $exclusions
541 *
542 * @return mixed
543 */
544 public static function appendStyleExclusionsArray($exclusions)
545 {
546 return self::mergePatternsIntoArray($exclusions, self::styleExclusionPatterns());
547 }
548
549 /**
550 * Append script patterns to a comma-separated string filter (Autoptimize).
551 *
552 * @param mixed $exclusions
553 *
554 * @return mixed
555 */
556 public static function appendExclusionsString($exclusions)
557 {
558 return self::mergePatternsIntoString($exclusions, self::exclusionPatterns());
559 }
560
561 /**
562 * Append style patterns to a comma-separated string filter (Autoptimize).
563 *
564 * @param mixed $exclusions
565 *
566 * @return mixed
567 */
568 public static function appendStyleExclusionsString($exclusions)
569 {
570 return self::mergePatternsIntoString($exclusions, self::styleExclusionPatterns());
571 }
572
573 /**
574 * @param mixed $exclusions
575 * @param array $patterns
576 *
577 * @return mixed
578 */
579 private static function mergePatternsIntoArray($exclusions, $patterns)
580 {
581 if (!is_array($exclusions)) {
582 return $exclusions;
583 }
584 return array_values(array_unique(array_merge($exclusions, $patterns)));
585 }
586
587 /**
588 * @param mixed $exclusions
589 * @param array $patterns
590 *
591 * @return mixed
592 */
593 private static function mergePatternsIntoString($exclusions, $patterns)
594 {
595 if (!is_string($exclusions)) {
596 return $exclusions;
597 }
598 $parts = array_filter(array_map('trim', explode(',', $exclusions)));
599 $parts = array_unique(array_merge($parts, $patterns));
600 return implode(',', $parts);
601 }
602
603 /**
604 * Append script handles to a handle-based filter (SiteGround).
605 *
606 * SiteGround matches exact handle names and our per-form inline handles are
607 * dynamic (bitform-bf-globals-bitforms_15_123_1), so scan the registered
608 * queue rather than passing prefixes, which it would not match.
609 *
610 * @param mixed $handles
611 *
612 * @return mixed
613 */
614 public static function appendHandles($handles)
615 {
616 return self::mergeRegisteredHandles($handles, self::HANDLE_PREFIXES, wp_scripts());
617 }
618
619 /**
620 * Append style handles to a handle-based filter (SiteGround).
621 *
622 * @param mixed $handles
623 *
624 * @return mixed
625 */
626 public static function appendStyleHandles($handles)
627 {
628 return self::mergeRegisteredHandles($handles, self::STYLE_HANDLE_PREFIXES, wp_styles());
629 }
630
631 /**
632 * @param mixed $handles
633 * @param array $prefixes
634 * @param mixed $registry a WP_Scripts or WP_Styles instance
635 *
636 * @return mixed
637 */
638 private static function mergeRegisteredHandles($handles, $prefixes, $registry)
639 {
640 if (!is_array($handles)) {
641 return $handles;
642 }
643 $bitformHandles = $prefixes;
644 if (is_object($registry) && !empty($registry->registered)) {
645 foreach (array_keys($registry->registered) as $registeredHandle) {
646 if (self::hasPrefix($registeredHandle, $prefixes)) {
647 $bitformHandles[] = $registeredHandle;
648 }
649 }
650 }
651 return array_values(array_unique(array_merge($handles, $bitformHandles)));
652 }
653
654 /**
655 * Whether a script handle belongs to Bit Form's frontend runtime.
656 *
657 * @param mixed $handle
658 *
659 * @return bool
660 */
661 private static function isBitformHandle($handle)
662 {
663 return self::hasPrefix($handle, self::HANDLE_PREFIXES);
664 }
665
666 /**
667 * Whether a style handle belongs to a Bit Form stylesheet.
668 *
669 * @param mixed $handle
670 *
671 * @return bool
672 */
673 private static function isBitformStyleHandle($handle)
674 {
675 return self::hasPrefix($handle, self::STYLE_HANDLE_PREFIXES);
676 }
677
678 /**
679 * @param mixed $handle
680 * @param array $prefixes
681 *
682 * @return bool
683 */
684 private static function hasPrefix($handle, $prefixes)
685 {
686 if (!is_string($handle) || '' === $handle) {
687 return false;
688 }
689 foreach ($prefixes as $prefix) {
690 if (0 === strpos($handle, $prefix)) {
691 return true;
692 }
693 }
694 return false;
695 }
696
697 /**
698 * Whether a tag or file path carries one of the given exclusion patterns.
699 *
700 * @param mixed $value
701 * @param array $patterns
702 *
703 * @return bool
704 */
705 private static function matchesAnyPattern($value, $patterns)
706 {
707 if (!is_string($value) || '' === $value) {
708 return false;
709 }
710 foreach ($patterns as $pattern) {
711 if (is_string($pattern) && '' !== $pattern && false !== strpos($value, $pattern)) {
712 return true;
713 }
714 }
715 return false;
716 }
717
718 /**
719 * Return false for Bit Form script handles on boolean, handle-keyed optimizer
720 * filters (js_do_concat).
721 *
722 * @param mixed $value
723 * @param mixed $handle
724 *
725 * @return mixed
726 */
727 public static function denyForBitformHandle($value, $handle = '')
728 {
729 return self::isBitformHandle($handle) ? false : $value;
730 }
731
732 /**
733 * Same, for style handles (css_do_concat).
734 *
735 * @param mixed $value
736 * @param mixed $handle
737 *
738 * @return mixed
739 */
740 public static function denyForBitformStyleHandle($value, $handle = '')
741 {
742 return self::isBitformStyleHandle($handle) ? false : $value;
743 }
744
745 /**
746 * Same, for filters that carry scripts and styles through one hook
747 * (Hummingbird's minify/combine/defer/inline).
748 *
749 * @param mixed $value
750 * @param mixed $handle
751 *
752 * @return mixed
753 */
754 public static function denyForBitformAsset($value, $handle = '')
755 {
756 return self::isBitformHandle($handle) || self::isBitformStyleHandle($handle) ? false : $value;
757 }
758
759 /**
760 * Deny W3 Total Cache tag minification for Bit Form scripts.
761 *
762 * @param mixed $doMinification
763 * @param mixed $scriptTag
764 * @param mixed $file
765 *
766 * @return mixed
767 */
768 public static function denyTagMinification($doMinification, $scriptTag = '', $file = '')
769 {
770 if (self::matchesAnyPattern($scriptTag, self::exclusionPatterns())
771 || self::matchesAnyPattern($file, self::exclusionPatterns())) {
772 return false;
773 }
774 return $doMinification;
775 }
776
777 /**
778 * Deny W3 Total Cache tag minification for Bit Form stylesheets.
779 *
780 * @param mixed $doMinification
781 * @param mixed $styleTag
782 * @param mixed $file
783 *
784 * @return mixed
785 */
786 public static function denyStyleTagMinification($doMinification, $styleTag = '', $file = '')
787 {
788 if (self::matchesAnyPattern($styleTag, self::styleExclusionPatterns())
789 || self::matchesAnyPattern($file, self::styleExclusionPatterns())) {
790 return false;
791 }
792 return $doMinification;
793 }
794
795 /**
796 * Stamp exclusion attributes on Bit Form <script src> tags. Honored by
797 * Cloudflare Rocket Loader, WP Rocket, LiteSpeed, FlyingPress, Breeze,
798 * NitroPack (nitro-exclude) and Jetpack Boost (data-jetpack-boost).
799 *
800 * @param mixed $tag
801 * @param mixed $handle
802 *
803 * @return mixed
804 */
805 public static function addNoOptimizeAttributes($tag, $handle)
806 {
807 if (!is_string($tag) || !self::isBitformHandle($handle)) {
808 return $tag;
809 }
810 if (false !== strpos($tag, 'data-no-optimize')) {
811 return $tag;
812 }
813 return str_replace('<script ', '<script ' . self::NO_OPTIMIZE_ATTRIBUTES, $tag);
814 }
815
816 /**
817 * Stamp the same attributes on Bit Form stylesheet tags. Covers both the
818 * <link> a registered sheet prints and the <style> an inline-only handle
819 * prints, since optimizers read either one.
820 *
821 * @param mixed $tag
822 * @param mixed $handle
823 *
824 * @return mixed
825 */
826 public static function addStyleNoOptimizeAttributes($tag, $handle)
827 {
828 if (!is_string($tag) || !self::isBitformStyleHandle($handle)) {
829 return $tag;
830 }
831 if (false !== strpos($tag, 'data-no-optimize')) {
832 return $tag;
833 }
834 $tag = str_replace('<link ', '<link ' . self::NO_OPTIMIZE_ATTRIBUTES, $tag);
835 return str_replace('<style ', '<style ' . self::NO_OPTIMIZE_ATTRIBUTES, $tag);
836 }
837
838 /**
839 * Same attributes for wp_add_inline_script fragments, which never pass
840 * through script_loader_tag. WP 6.3+.
841 *
842 * @param mixed $attributes
843 * @param mixed $data
844 *
845 * @return mixed
846 */
847 public static function addInlineNoOptimizeAttributes($attributes, $data = '')
848 {
849 if (!is_array($attributes)) {
850 return $attributes;
851 }
852 $id = isset($attributes['id']) ? $attributes['id'] : '';
853 $isBitformInline = (is_string($id) && 0 === strpos($id, 'bitform'))
854 || (is_string($data) && false !== strpos($data, 'bf_globals'));
855 if ($isBitformInline) {
856 $attributes['data-no-optimize'] = '1';
857 $attributes['data-no-defer'] = '1';
858 $attributes['data-no-minify'] = '1';
859 $attributes['data-cfasync'] = 'false';
860 $attributes['data-jetpack-boost'] = 'ignore';
861 $attributes['nowprocket'] = true;
862 $attributes['nitro-exclude'] = true;
863 }
864 return $attributes;
865 }
866 }
867