PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / trunk
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score vtrunk
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
← All changes | includes/classes/LazyLoad.php +235 -14 2.2.1trunk View file →
@@ -10,8 +10,10 @@
10 10 namespace PoweredCache;
11 11
12 12 use const PoweredCache\Constants\POST_META_DISABLE_LAZYLOAD_KEY;
13 13
14 +// phpcs:disable WordPressVIPMinimum.Security.ProperEscapingFunction.hrefSrcEscUrl
15 +
14 16 /**
15 17 * Class LazyLoad
16 18 */
17 19 class LazyLoad {
@@ -20,9 +22,9 @@
20 22 * Hold plugin settings
21 23 *
22 24 * @var array $settings
23 25 */
24 - private $settings;
26 + private static $settings = null;
25 27
26 28 /**
27 29 * Return an instance of the current class
28 30 *
@@ -42,14 +44,18 @@
42 44 /**
43 45 * Setup routine
44 46 */
45 47 public function setup() {
46 - $this->settings = \PoweredCache\Utils\get_settings();
48 + if ( ! self::$settings ) {
49 + self::$settings = \PoweredCache\Utils\get_settings();
50 + }
47 51
48 - if ( $this->settings['enable_lazy_load'] ) {
52 + if ( self::$settings['enable_lazy_load'] ) {
49 53 add_action( 'wp', [ $this, 'init' ], 9999 ); // run this as late as possible
50 54 add_action( 'powered_cache_lazy_load_compat', [ $this, 'compat' ] );
51 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' ] );
52 58 }
53 59
54 60 /**
55 61 * Filter to disable native lazyload support.
@@ -60,10 +66,10 @@
60 66 *
61 67 * @return {boolean} New value.
62 68 * @since 2.0
63 69 */
64 - if ( apply_filters( 'powered_cache_disable_native_lazyload', $this->settings['disable_wp_lazy_load'] ) ) {
65 - add_filter( 'wp_lazy_loading_enabled', '__return_false' );
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 );
66 72 }
67 73
68 74 }
69 75
@@ -121,11 +127,37 @@
121 127 * @since 1.0
122 128 */
123 129 $threshold = apply_filters( 'powered_cache_lazy_load_threshold', 200 );
124 130
125 - if ( 200 !== (int) $threshold ) {
126 - wp_localize_script( 'PCLL', 'PCLL_options', array( 'threshold' => $threshold ) );
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 + );
127 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 +
128 160 }
129 161
130 162 /**
131 163 * Set up filtering for certain content
@@ -141,9 +173,9 @@
141 173 *
142 174 * @return {boolean} New value.
143 175 * @since 1.0
144 176 */
145 - if ( true === apply_filters( 'powered_cache_lazy_load_images', $this->settings['lazy_load_images'] ) ) {
177 + if ( true === apply_filters( 'powered_cache_lazy_load_images', self::$settings['lazy_load_images'] ) ) {
146 178 add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'filter_images' ) );
147 179 }
148 180
149 181 /**
@@ -155,9 +187,9 @@
155 187 *
156 188 * @return {boolean} New value.
157 189 * @since 1.0
158 190 */
159 - if ( true === apply_filters( 'powered_cache_lazy_load_iframes', $this->settings['lazy_load_iframes'] ) ) {
191 + if ( true === apply_filters( 'powered_cache_lazy_load_iframes', self::$settings['lazy_load_iframes'] ) ) {
160 192 add_filter( 'powered_cache_lazy_load_filter', array( __CLASS__, 'filter_iframes' ) );
161 193 }
162 194
163 195 /**
@@ -169,9 +201,9 @@
169 201 *
170 202 * @return {boolean} New value.
171 203 * @since 1.0
172 204 */
173 - if ( true === apply_filters( 'powered_cache_lazy_load_post_content', $this->settings['lazy_load_post_content'] ) ) {
205 + if ( true === apply_filters( 'powered_cache_lazy_load_post_content', self::$settings['lazy_load_post_content'] ) ) {
174 206 add_filter( 'the_content', array( __CLASS__, 'filter' ), 200 );
175 207 }
176 208
177 209 /**
@@ -183,9 +215,9 @@
183 215 *
184 216 * @return {boolean} New value.
185 217 * @since 1.0
186 218 */
187 - if ( true === apply_filters( 'powered_cache_lazy_load_widget_text', $this->settings['lazy_load_widgets'] ) ) {
219 + if ( true === apply_filters( 'powered_cache_lazy_load_widget_text', self::$settings['lazy_load_widgets'] ) ) {
188 220 add_filter( 'widget_text', array( __CLASS__, 'filter' ), 200 );
189 221 }
190 222
191 223 /**
@@ -197,13 +229,27 @@
197 229 *
198 230 * @return {boolean} New value.
199 231 * @since 1.0
200 232 */
201 - if ( true === apply_filters( 'powered_cache_lazy_load_post_thumbnail', $this->settings['lazy_load_post_thumbnail'] ) ) {
233 + if ( true === apply_filters( 'powered_cache_lazy_load_post_thumbnail', self::$settings['lazy_load_post_thumbnail'] ) ) {
202 234 add_filter( 'post_thumbnail_html', array( __CLASS__, 'filter' ), 200 );
203 235 }
204 236
205 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 + /**
206 252 * Filters whether apply or not apply lazyload for avatars.
207 253 *
208 254 * @hook powered_cache_lazy_load_avatar
209 255 *
@@ -211,12 +257,22 @@
211 257 *
212 258 * @return {boolean} New value.
213 259 * @since 1.0
214 260 */
215 - if ( true === apply_filters( 'powered_cache_lazy_load_avatar', $this->settings['lazy_load_avatars'] ) ) {
261 + if ( true === apply_filters( 'powered_cache_lazy_load_avatar', self::$settings['lazy_load_avatars'] ) ) {
216 262 add_filter( 'get_avatar', array( __CLASS__, 'filter' ), 200 );
217 263 }
218 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 + */
219 275 add_filter( 'powered_cache_lazy_load_html', array( __CLASS__, 'filter' ) );
220 276 }
221 277
222 278 /**
@@ -287,8 +343,11 @@
287 343 $search = array();
288 344 $replace = array();
289 345
290 346 foreach ( $matches[0] as $img_html ) {
347 + if ( self::is_excluded( $img_html ) ) {
348 + continue;
349 + }
291 350
292 351 // don't to the replacement if the image is a data-uri
293 352 if ( ! preg_match( "/src=['\"]data:image/is", $img_html ) ) {
294 353
@@ -348,8 +407,12 @@
348 407 $replace = array();
349 408
350 409 foreach ( $matches[0] as $iframe_html ) {
351 410
411 + if ( self::is_excluded( $iframe_html ) ) {
412 + continue;
413 + }
414 +
352 415 // Don't mess with the Gravity Forms ajax iframe
353 416 if ( strpos( $iframe_html, 'gform_ajax_frame' ) ) {
354 417 continue;
355 418 }
@@ -458,9 +521,9 @@
458 521 if ( function_exists( 'mopr_get_option' ) && WP_CONTENT_DIR . mopr_get_option( 'mobile_theme_root', 1 ) === get_theme_root() ) {
459 522 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
460 523 }
461 524
462 - if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( $_SERVER['HTTP_USER_AGENT'], 'Opera Mini' ) ) {
525 + if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( $_SERVER['HTTP_USER_AGENT'], 'Opera Mini' ) ) { // phpcs:ignore
463 526 add_filter( 'powered_cache_lazy_load_enabled', '__return_false' );
464 527 }
465 528
466 529 if ( 1 === intval( get_query_var( 'print' ) ) || 1 === intval( get_query_var( 'printpage' ) ) ) {
@@ -486,8 +549,166 @@
486 549 $status = false;
487 550 }
488 551
489 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;
490 711 }
491 712
492 713 }
493 714