PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 3.7
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v3.7
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / classes / LazyLoad.php

LazyLoad.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score 3.7, at includes/classes/LazyLoad.php

715 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * LazyLoad
4 *
5 * Most of the code borrowed from https://github.com/Angrycreative/bj-lazy-load
6 *
7 * @package PoweredCache
8 */
9
10 namespace PoweredCache;
11
12 use const PoweredCache\Constants\POST_META_DISABLE_LAZYLOAD_KEY;
13
14 // phpcs:disable WordPressVIPMinimum.Security.ProperEscapingFunction.hrefSrcEscUrl
15
16 /**
17 * Class LazyLoad
18 */
19 class LazyLoad {
20
21 /**
22 * Hold plugin settings
23 *
24 * @var array $settings
25 */
26 private static $settings = null;
27
28 /**
29 * Return an instance of the current class
30 *
31 * @return LazyLoad
32 */
33 public static function factory() {
34 static $instance = false;
35
36 if ( ! $instance ) {
37 $instance = new self();
38 $instance->setup();
39 }
40
41 return $instance;
42 }
43
44 /**
45 * Setup routine
46 */
47 public function setup() {
48 if ( ! self::$settings ) {
49 self::$settings = \PoweredCache\Utils\get_settings();
50 }
51
52 if ( self::$settings['enable_lazy_load'] ) {
53 add_action( 'wp', [ $this, 'init' ], 9999 ); // run this as late as possible
54 add_action( 'powered_cache_lazy_load_compat', [ $this, 'compat' ] );
55 add_action( 'powered_cache_lazy_load_run_filter', [ $this, 'maybe_disable_through_meta' ] );
56 add_filter( 'powered_cache_delayed_js_skip', [ $this, 'delayed_js_skip' ], 10, 2 );
57 add_filter( 'powered_cache_fo_excluded_js_files', [ $this, 'add_file_optimizer_exclusion' ] );
58 }
59
60 /**
61 * Filter to disable native lazyload support.
62 *
63 * @hook powered_cache_disable_native_lazyload
64 *
65 * @param {boolean} $status true to disable native lazy load.
66 *
67 * @return {boolean} New value.
68 * @since 2.0
69 */
70 if ( apply_filters( 'powered_cache_disable_native_lazyload', self::$settings['disable_wp_lazy_load'] ) ) {
71 add_filter( 'wp_lazy_loading_enabled', '__return_false', 9999 );
72 }
73
74 }
75
76 /**
77 * Initialize the setup
78 */
79 public function init() {
80 /* We do not touch the feeds */
81 if ( is_admin() || is_feed() || is_preview() ) {
82 return;
83 }
84
85 /**
86 * Fires before filtering lazy-load hooks.
87 *
88 * @hook powered_cache_lazy_load_compat
89 *
90 * @since 1.0
91 */
92 do_action( 'powered_cache_lazy_load_compat' );
93
94 /**
95 * Filters lazy-load status.
96 *
97 * @hook powered_cache_lazy_load_enabled
98 *
99 * @param {boolean} $enable true to enable
100 *
101 * @return {boolean} New value
102 * @since 1.0
103 */
104 $enabled = apply_filters( 'powered_cache_lazy_load_enabled', true );
105
106 if ( $enabled ) {
107 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
108
109 $this->setup_filtering();
110 }
111 }
112
113 /**
114 * Enqueue scripts
115 */
116 public function enqueue_scripts() {
117 wp_enqueue_script( 'PCLL', POWERED_CACHE_URL . 'dist/js/lazyload.js', null, POWERED_CACHE_VERSION, true );
118
119 /**
120 * Filters screen threshold value.
121 *
122 * @hook powered_cache_lazy_load_threshold
123 *
124 * @param {int} $threshold Screen display threshold.
125 *
126 * @return {int} New value
127 * @since 1.0
128 */
129 $threshold = apply_filters( 'powered_cache_lazy_load_threshold', 200 );
130
131 /**
132 * Filters the count of images that skipped from lazyload.
133 *
134 * @hook powered_cache_lazy_load_skip_first_nth_img
135 *
136 * @param {int} $immediate_load_count Default image count
137 *
138 * @return {int} New value
139 * @since 3.1
140 */
141 $immediate_load_count = apply_filters( 'powered_cache_lazy_load_skip_first_nth_img', self::$settings['lazy_load_skip_first_nth_img'] );
142
143 if ( 200 !== (int) $threshold || 3 !== (int) $immediate_load_count ) {
144 wp_localize_script(
145 'PCLL',
146 'PCLL_options',
147 [
148 'threshold' => $threshold,
149 'immediate_load_count' => $immediate_load_count,
150 ]
151 );
152 }
153
154 if ( self::$settings['lazy_load_youtube'] ) {
155 wp_register_style( 'pcll-youtube-lazyload', false ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
156 wp_enqueue_style( 'pcll-youtube-lazyload' );
157 wp_add_inline_style( 'pcll-youtube-lazyload', $this->add_inline_youtube_css() );
158 }
159
160 }
161
162 /**
163 * Set up filtering for certain content
164 */
165 protected function setup_filtering() {
166
167 /**
168 * Filters whether apply or not apply lazyload for images.
169 *
170 * @hook powered_cache_lazy_load_images
171 *
172 * @param {boolean} true to lazyload for images
173 *
174 * @return {boolean} New value.
175 * @since 1.0
176 */
177 if ( true === apply_filters( 'powered_cache_lazy_load_images', self::$settings['lazy_load_images'] ) ) {
178 add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'filter_images' ) );
179 }
180
181 /**
182 * Filters whether apply or not apply lazyload for iframes.
183 *
184 * @hook powered_cache_lazy_load_iframes
185 *
186 * @param {boolean} true to apply lazyload for iframes
187 *
188 * @return {boolean} New value.
189 * @since 1.0
190 */
191 if ( true === apply_filters( 'powered_cache_lazy_load_iframes', self::$settings['lazy_load_iframes'] ) ) {
192 add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'filter_iframes' ) );
193 }
194
195 /**
196 * Filters whether apply or not apply lazyload for post_content.
197 *
198 * @hook powered_cache_lazy_load_post_content
199 *
200 * @param {boolean} true to apply lazyload for post_content
201 *
202 * @return {boolean} New value.
203 * @since 1.0
204 */
205 if ( true === apply_filters( 'powered_cache_lazy_load_post_content', self::$settings['lazy_load_post_content'] ) ) {
206 add_filter( 'the_content', array( __CLASS__, 'filter' ), 200 );
207 }
208
209 /**
210 * Filters whether apply or not apply lazyload for widgets.
211 *
212 * @hook powered_cache_lazy_load_widget_text
213 *
214 * @param {boolean} true to apply lazyload for widgets
215 *
216 * @return {boolean} New value.
217 * @since 1.0
218 */
219 if ( true === apply_filters( 'powered_cache_lazy_load_widget_text', self::$settings['lazy_load_widgets'] ) ) {
220 add_filter( 'widget_text', array( __CLASS__, 'filter' ), 200 );
221 }
222
223 /**
224 * Filters whether apply or not apply lazyload for thumbnails.
225 *
226 * @hook powered_cache_lazy_load_post_thumbnail
227 *
228 * @param {boolean} true to apply lazyload for thumbnails
229 *
230 * @return {boolean} New value.
231 * @since 1.0
232 */
233 if ( true === apply_filters( 'powered_cache_lazy_load_post_thumbnail', self::$settings['lazy_load_post_thumbnail'] ) ) {
234 add_filter( 'post_thumbnail_html', array( __CLASS__, 'filter' ), 200 );
235 }
236
237 /**
238 * Filters whether replace youtube iframe with thumbnail.
239 *
240 * @hook powered_cache_lazy_load_youtube
241 *
242 * @param {boolean} true to replace youtube iframe with thumbnail
243 *
244 * @return {boolean} New value.
245 * @since 3.4
246 */
247 if ( true === apply_filters( 'powered_cache_lazy_load_youtube', self::$settings['lazy_load_youtube'] ) ) {
248 add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'replace_youtube_iframe_with_thumbnail' ), 9 );
249 }
250
251 /**
252 * Filters whether apply or not apply lazyload for avatars.
253 *
254 * @hook powered_cache_lazy_load_avatar
255 *
256 * @param {boolean} true to apply lazyload for avatars
257 *
258 * @return {boolean} New value.
259 * @since 1.0
260 */
261 if ( true === apply_filters( 'powered_cache_lazy_load_avatar', self::$settings['lazy_load_avatars'] ) ) {
262 add_filter( 'get_avatar', array( __CLASS__, 'filter' ), 200 );
263 }
264
265 /**
266 * * Filters whether apply or not apply lazyload for html.
267 *
268 * @hook powered_cache_lazy_load_html
269 *
270 * @param {boolean} true to apply lazyload for html
271 *
272 * @return {boolean} New value.
273 * @since 1.0
274 */
275 add_filter( 'powered_cache_lazy_load_html', array( __CLASS__, 'filter' ) );
276 }
277
278 /**
279 * Filter HTML content. Replace supported content with placeholders.
280 *
281 * @param string $content The HTML string to filter
282 *
283 * @return string The filtered HTML string
284 */
285 public static function filter( $content ) {
286
287 // Last chance to bail out before running the filter
288 /**
289 * Filters lazy-load run filter.
290 *
291 * @hook powered_cache_lazy_load_run_filter
292 *
293 * @param {boolean} $run_filter true to enable
294 *
295 * @return {boolean} New value
296 * @since 1.0
297 */
298 $run_filter = apply_filters( 'powered_cache_lazy_load_run_filter', true );
299 if ( ! $run_filter ) {
300 return $content;
301 }
302
303 /**
304 * Filters the content
305 *
306 * @hook powered_cache_lazy_load_filter
307 *
308 * @param string $content The HTML string to filter
309 *
310 * @since 1.0
311 */
312 $content = apply_filters( 'powered_cache_lazy_load_filter', $content );
313
314 return $content;
315 }
316
317
318 /**
319 * Replace images with placeholders in the content
320 *
321 * @param string $content The HTML to do the filtering on
322 *
323 * @return string The HTML with the images replaced
324 */
325 public static function filter_images( $content ) {
326
327 /**
328 * Filters the content
329 *
330 * @hook powered_cache_lazy_load_filter
331 *
332 * @param string $content The HTML string to filter
333 *
334 * @since 1.0
335 */
336 $placeholder_url = apply_filters( 'powered_cache_lazy_load_placeholder_url', 'data:image/gif;base64,R0lGODdhAQABAPAAAP///wAAACwAAAAAAQABAEACAkQBADs=' );
337
338 $match_content = self::get_content_haystack( $content );
339
340 $matches = array();
341 preg_match_all( '/<img[\s\r\n]+.*?>/is', $match_content, $matches );
342
343 $search = array();
344 $replace = array();
345
346 foreach ( $matches[0] as $img_html ) {
347 if ( self::is_excluded( $img_html ) ) {
348 continue;
349 }
350
351 // don't to the replacement if the image is a data-uri
352 if ( ! preg_match( "/src=['\"]data:image/is", $img_html ) ) {
353
354 $placeholder_url_used = $placeholder_url;
355
356 // replace the src and add the data-src attribute
357 $replace_html = preg_replace( '/<img(.*?)src=/is', '<img$1src="' . esc_attr( $placeholder_url_used ) . '" data-lazy-type="image" data-lazy-src=', $img_html );
358
359 // also replace the srcset (responsive images)
360 $replace_html = str_replace( 'srcset', 'data-lazy-srcset', $replace_html );
361
362 // add the lazy class to the img element
363 if ( preg_match( '/class=["\']/i', $replace_html ) ) {
364 $replace_html = preg_replace( '/class=(["\'])(.*?)["\']/is', 'class=$1lazy lazy-hidden $2$1', $replace_html );
365 } else {
366 $replace_html = preg_replace( '/<img/is', '<img class="lazy lazy-hidden"', $replace_html );
367 }
368
369 $replace_html .= '<noscript>' . $img_html . '</noscript>';
370
371 array_push( $search, $img_html );
372 array_push( $replace, $replace_html );
373 }
374 }
375
376 $content = str_replace( $search, $replace, $content );
377
378 return $content;
379
380 }
381
382 /**
383 * Replace iframes with placeholders in the content
384 *
385 * @param string $content The HTML to do the filtering on
386 *
387 * @return string The HTML with the iframes replaced
388 */
389 public static function filter_iframes( $content ) {
390 /**
391 * Filters the lazyload placeholder URL.
392 *
393 * @hook powered_cache_lazy_load_placeholder_url
394 *
395 * @param string $image default placeholder url. Base64 string as default.
396 *
397 * @since 1.0
398 */
399 $placeholder_url = apply_filters( 'powered_cache_lazy_load_placeholder_url', 'data:image/gif;base64,R0lGODdhAQABAPAAAP///wAAACwAAAAAAQABAEACAkQBADs=' );
400
401 $match_content = self::get_content_haystack( $content );
402
403 $matches = array();
404 preg_match_all( '|<iframe\s+.*?</iframe>|si', $match_content, $matches );
405
406 $search = array();
407 $replace = array();
408
409 foreach ( $matches[0] as $iframe_html ) {
410
411 if ( self::is_excluded( $iframe_html ) ) {
412 continue;
413 }
414
415 // Don't mess with the Gravity Forms ajax iframe
416 if ( strpos( $iframe_html, 'gform_ajax_frame' ) ) {
417 continue;
418 }
419
420 $replace_html = '<img src="' . esc_attr( $placeholder_url ) . '" class="lazy lazy-hidden" data-lazy-type="iframe" data-lazy-src="' . esc_attr( $iframe_html ) . '" alt="">';
421
422 $replace_html .= '<noscript>' . $iframe_html . '</noscript>';
423
424 array_push( $search, $iframe_html );
425 array_push( $replace, $replace_html );
426 }
427
428 $content = str_replace( $search, $replace, $content );
429
430 return $content;
431
432 }
433
434 /**
435 * Remove elements we don’t want to filter from the HTML string
436 * We’re reducing the haystack by removing the hay we know we don’t want to look for needles in
437 *
438 * @param string $content The HTML string
439 *
440 * @return string The HTML string without the unwanted elements
441 */
442 protected static function get_content_haystack( $content ) {
443 $content = self::remove_noscript( $content );
444 $content = self::remove_skip_classes_elements( $content );
445
446 return $content;
447 }
448
449 /**
450 * Remove <noscript> elements from HTML string
451 *
452 * @param string $content The HTML string
453 *
454 * @return string The HTML string without <noscript> elements
455 * @author sigginet
456 */
457 public static function remove_noscript( $content ) {
458 return preg_replace( '/<noscript.*?(\/noscript>)/i', '', $content );
459 }
460
461 /**
462 * Remove HTML elements with certain classnames (or IDs) from HTML string
463 *
464 * @param string $content The HTML string
465 *
466 * @return string The HTML string without the unwanted elements
467 */
468 public static function remove_skip_classes_elements( $content ) {
469
470 $skip_classes = self::get_skip_classes( 'html' );
471
472 /**
473 * http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454
474 * We can’t do this, but we still do it.
475 */
476 $skip_classes_quoted = array_map( 'preg_quote', $skip_classes );
477 $skip_classes_ored = implode( '|', $skip_classes_quoted );
478
479 $regex = '/<\s*\w*\s*class\s*=\s*[\'"]?(|.*\s)?' . $skip_classes_ored . '(|\s.*)?[\'"]?.*?>/isU';
480
481 return preg_replace( $regex, '', $content );
482 }
483
484
485 /**
486 * Get the skip classes
487 *
488 * @param string $content_type The content type (image/iframe etc)
489 *
490 * @return array An array of strings with the class names
491 */
492 protected static function get_skip_classes( $content_type ) {
493 /**
494 * Filters the class names to skip
495 *
496 * @hook powered_cache_lazy_load_skip_classes
497 *
498 * @param {array} $skip_classes The current classes to skip
499 * @param {string} $content_type The current content type
500 *
501 * @return {array} New value.
502 * @since 1.0
503 */
504 $skip_classes = apply_filters( 'powered_cache_lazy_load_skip_classes', array( 'lazy' ), $content_type );
505
506 return $skip_classes;
507 }
508
509 /**
510 * Manage 3rd party compat cases
511 */
512 public function compat() {
513 if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
514 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
515 }
516
517 if ( function_exists( 'bp_is_my_profile' ) && bp_is_my_profile() ) {
518 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
519 }
520
521 if ( function_exists( 'mopr_get_option' ) && WP_CONTENT_DIR . mopr_get_option( 'mobile_theme_root', 1 ) === get_theme_root() ) {
522 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
523 }
524
525 if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( $_SERVER['HTTP_USER_AGENT'], 'Opera Mini' ) ) { // phpcs:ignore
526 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
527 }
528
529 if ( 1 === intval( get_query_var( 'print' ) ) || 1 === intval( get_query_var( 'printpage' ) ) ) {
530 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
531 }
532
533 if ( function_exists( 'bnc_wptouch_is_mobile' ) || defined( 'WPTOUCH_VERSION' ) ) {
534 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
535 }
536 }
537
538 /**
539 * Maybe disable lazyloading for a particular post
540 *
541 * @param bool $status Lazyload filter status
542 *
543 * @return bool
544 * @since 2.0
545 */
546 public function maybe_disable_through_meta( $status ) {
547
548 if ( in_the_loop() && get_post_meta( get_the_ID(), POST_META_DISABLE_LAZYLOAD_KEY, true ) ) {
549 $status = false;
550 }
551
552 return $status;
553 }
554
555 /**
556 * Skip lazyload for delayed script
557 *
558 * @param boolean $is_delay_skipped Whether skip or not skip delayed JS
559 * @param string $script script
560 *
561 * @return boolean
562 */
563 public function delayed_js_skip( $is_delay_skipped, $script ) {
564 if ( false !== stripos( $script, 'powered-cache/dist/js/lazyload.js' ) || false !== stripos( $script, 'PCLL_' ) ) {
565 return true;
566 }
567
568 return $is_delay_skipped;
569 }
570
571 /**
572 * Exclude link preloader from file optimizer
573 *
574 * @param array $excluded_files the list of excluded files for optimization
575 *
576 * @return mixed
577 */
578 public function add_file_optimizer_exclusion( $excluded_files ) {
579 $excluded_files[] = POWERED_CACHE_URL . 'dist/js/lazyload.js';
580
581 return $excluded_files;
582 }
583
584 /**
585 * Replace youtube iframe with thumbnail
586 *
587 * @param string $content HTML content, or the content of the current post if called in the loop.
588 *
589 * @return array|string|string[]|null
590 * @since 3.4
591 */
592 public static function replace_youtube_iframe_with_thumbnail( $content ) {
593 $match_content = self::get_content_haystack( $content );
594
595 // Regular expression to match YouTube iframes
596 $pattern = '/<iframe[^>]+src="https?:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]+)([^"]*)"[^>]*><\/iframe>/i';
597
598 // Find all YouTube iframes
599 preg_match_all( $pattern, $match_content, $matches );
600
601 $search = array();
602 $replace = array();
603
604 foreach ( $matches[0] as $iframe_html ) {
605 if ( self::is_excluded( $iframe_html ) ) {
606 continue;
607 }
608
609 // Extract video ID and additional parameters
610 preg_match( $pattern, $iframe_html, $iframe_parts );
611 $video_id = $iframe_parts[1];
612 $params = $iframe_parts[2];
613
614 // Construct the replacement HTML using the video ID
615 $replacement = '<div class="pcll-youtube-player" data-src="https://www.youtube.com/embed/' . $video_id . $params . '">
616 <img src="https://img.youtube.com/vi/' . $video_id . '/0.jpg" style="width:100%;height:auto;">
617 <div style="width: 100%; height: 100%;cursor:pointer;">
618 <svg class="pcll-youtube-play-button" viewBox="0 0 68 48" style="position: absolute; left: 50%; top: 50%; width: 68px; height: 48px; margin-left: -34px; margin-top: -24px; transition: opacity .25s cubic-bezier(0,0,.2,1); z-index: 63; cursor: pointer;">
619 <path d="M66.52,7.74c-0.78-2.93-3.07-5.22-6-6C53.08,0.74,34,0.74,34,0.74s-19.08,0-26.52,1C4.57,2.52,2.28,4.81,1.5,7.74C0.68,11,0.68,24,0.68,24s0,13,0.82,16.26c0.78,2.93,3.07,5.22,6,6c7.44,1,26.52,1,26.52,1s19.08,0,26.52-1c2.93-0.78,5.22-3.07,6-6C67.32,37,67.32,24,67.32,24S67.32,11,66.52,7.74z" fill-opacity="0.8" fill="#FF0000"></path>
620 <path d="M 45,24 27,14 27,34" fill="#fff"></path>
621 </svg>
622 </div>
623 </div>';
624
625 // Add original iframe HTML and replacement HTML to their respective arrays
626 array_push( $search, $iframe_html );
627 array_push( $replace, $replacement );
628 }
629
630 // Replace all YouTube iframes with their corresponding thumbnail placeholders
631 $content = str_replace( $search, $replace, $content );
632
633 return $content;
634 }
635
636
637 /**
638 * Add inline css for youtube lazyload
639 *
640 * @return string
641 * @since 3.4
642 */
643 public function add_inline_youtube_css() {
644 $css = '';
645
646 $yt_lazyload = wp_normalize_path( POWERED_CACHE_PATH . 'dist/css/lazyload-youtube.css' );
647 if ( file_exists( $yt_lazyload ) ) {
648 $css = (string) file_get_contents( $yt_lazyload ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
649 }
650
651 /**
652 * Filters the inline css for youtube lazyload
653 *
654 * @hook powered_cache_lazy_load_youtube_css
655 *
656 * @param string $css
657 *
658 * @return string
659 * @since 3.4
660 */
661 $css = apply_filters( 'powered_cache_lazy_load_youtube_css', $css );
662
663 return $css;
664 }
665
666
667 /**
668 * Get lazyload exclusion list
669 *
670 * @return array
671 * @since 3.4
672 */
673 public static function get_exclusions() {
674 if ( ! self::$settings ) {
675 self::$settings = \PoweredCache\Utils\get_settings();
676 }
677
678 $load_exclusions = preg_split( '#(\r\n|\n|\r)#', self::$settings['lazy_load_exclusions'], - 1, PREG_SPLIT_NO_EMPTY );
679 $load_exclusions[] = 'selectors.core.image.lightboxObjectFit'; // exclude core lightboxed images
680
681 /**
682 * Filter the lazyload exclusions
683 *
684 * @hook powered_cache_lazy_load_exclusions
685 *
686 * @param {array} $load_exclusions The current exclusions
687 *
688 * @return {array} New value
689 * @since 3.4
690 */
691 return (array) apply_filters( 'powered_cache_lazy_load_exclusions', $load_exclusions );
692 }
693
694 /**
695 * Check if excluded or not from defer
696 *
697 * @param string $tag Resource
698 *
699 * @return bool
700 * @since 3.4
701 */
702 public static function is_excluded( $tag ) {
703 $excluded_files = self::get_exclusions();
704 $excluded_files = implode( '|', $excluded_files );
705
706 if ( ! empty( $excluded_files ) && preg_match( '#(' . $excluded_files . ')#', $tag ) ) {
707 return true;
708 }
709
710 return false;
711 }
712
713 }
714
715