| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | +use OptimoleWP\PageProfiler\Profile; | |
| 4 | +use OptimoleWP\Preload\Links; | |
| 5 | + | |
| 3 | 6 | /** |
| 4 | 7 | * The class handles the img tag replacements for lazyload. |
| 5 | 8 | * |
| 6 | 9 | * @package \Optml\Inc |
| @@ -8,8 +11,9 @@ | ||
| 8 | 11 | */ |
| 9 | 12 | final class Optml_Lazyload_Replacer extends Optml_App_Replacer { |
| 10 | 13 | use Optml_Normalizer; |
| 11 | 14 | use Optml_Validator; |
| 15 | + use Optml_Dam_Offload_Utils; | |
| 12 | 16 | |
| 13 | 17 | const IFRAME_PLACEHOLDER_CLASS = ' |
| 14 | 18 | iframe[data-opt-src]:not([data-opt-lazy-loaded]) { |
| 15 | 19 | background-color: #ffffff; |
| @@ -26,9 +30,9 @@ | ||
| 26 | 30 | const IFRAME_PLACEHOLDER_STYLE = '<style type="text/css">' . self::IFRAME_PLACEHOLDER_CLASS . '</style>'; |
| 27 | 31 | |
| 28 | 32 | const IFRAME_TEMP_COMMENT = '/** optmliframelazyloadplaceholder */'; |
| 29 | 33 | |
| 30 | - const SVG_PLACEHOLDER = 'data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20#width#%20#height#%22%20width%3D%22#width#%22%20height%3D%22#height#%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3C%2Fsvg%3E'; | |
| 34 | + const SVG_PLACEHOLDER = 'data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20#width#%20#height#%22%20width%3D%22#width#%22%20height%3D%22#height#%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22#width#%22%20height%3D%22#height#%22%20fill%3D%22#fill#%22%2F%3E%3C%2Fsvg%3E'; | |
| 31 | 35 | /** |
| 32 | 36 | * If frame lazyload is present on page. |
| 33 | 37 | * |
| 34 | 38 | * @var bool Whether or not at least one iframe has been lazyloaded. |
| @@ -36,9 +40,9 @@ | ||
| 36 | 40 | private static $found_iframe = false; |
| 37 | 41 | /** |
| 38 | 42 | * Cached object instance. |
| 39 | 43 | * |
| 40 | - * @var Optml_Tag_Replacer | |
| 44 | + * @var Optml_Lazyload_Replacer | |
| 41 | 45 | */ |
| 42 | 46 | protected static $instance = null; |
| 43 | 47 | /** |
| 44 | 48 | * Holds the number of images to skip from lazyload. |
| @@ -81,13 +85,8 @@ | ||
| 81 | 85 | * |
| 82 | 86 | * @var bool Lazyload placeholder flag. |
| 83 | 87 | */ |
| 84 | 88 | private static $is_lazyload_placeholder = false; |
| 85 | - /** | |
| 86 | - * Holds flags which remove noscript tag bundle causing issues on render, i.e slider plugins. | |
| 87 | - * | |
| 88 | - * @var array Noscript flags. | |
| 89 | - */ | |
| 90 | 89 | |
| 91 | 90 | /** |
| 92 | 91 | * Class instance method. |
| 93 | 92 | * |
| @@ -92,9 +91,9 @@ | ||
| 92 | 91 | * Class instance method. |
| 93 | 92 | * |
| 94 | 93 | * @codeCoverageIgnore |
| 95 | 94 | * @static |
| 96 | - * @return Optml_Tag_Replacer | |
| 95 | + * @return Optml_Lazyload_Replacer | |
| 97 | 96 | * @since 1.0.0 |
| 98 | 97 | * @access public |
| 99 | 98 | */ |
| 100 | 99 | public static function instance() { |
| @@ -111,21 +110,26 @@ | ||
| 111 | 110 | * |
| 112 | 111 | * @return array Lazyload selectors. |
| 113 | 112 | */ |
| 114 | 113 | public static function get_background_lazyload_selectors() { |
| 114 | + if ( null !== self::$background_lazyload_selectors ) { | |
| 115 | + return self::$background_lazyload_selectors; | |
| 116 | + } | |
| 115 | 117 | |
| 116 | - if ( ! empty( self::$background_lazyload_selectors ) && is_array( self::$background_lazyload_selectors ) ) { | |
| 118 | + if ( self::instance()->settings === null ) { | |
| 119 | + self::$background_lazyload_selectors = []; | |
| 120 | + | |
| 117 | 121 | return self::$background_lazyload_selectors; |
| 118 | 122 | } |
| 119 | 123 | if ( self::instance()->settings->get( 'bg_replacer' ) === 'disabled' ) { |
| 120 | 124 | self::$background_lazyload_selectors = []; |
| 121 | - | |
| 122 | 125 | return self::$background_lazyload_selectors; |
| 123 | 126 | } |
| 124 | 127 | $default_watchers = [ |
| 125 | - '.elementor-section[data-settings*="background_background"]', | |
| 126 | - '.elementor-section > .elementor-background-overlay', | |
| 127 | - '.wp-block-cover[style*="background-image"]', | |
| 128 | + '[class*="wp-block-cover"][style*="background-image"]', | |
| 129 | + '[style*="background-image:url("]', '[style*="background-image: url("]', | |
| 130 | + '[style*="background:url("]', '[style*="background: url("]', | |
| 131 | + '[class*="wp-block-group"][style*="background-image"]', | |
| 128 | 132 | ]; |
| 129 | 133 | |
| 130 | 134 | $saved_watchers = self::instance()->settings->get_watchers(); |
| 131 | 135 | |
| @@ -150,10 +154,9 @@ | ||
| 150 | 154 | * |
| 151 | 155 | * @return array |
| 152 | 156 | */ |
| 153 | 157 | public static function get_lazyload_bg_classes() { |
| 154 | - | |
| 155 | - if ( ! empty( self::$lazyload_background_classes ) && is_array( self::$lazyload_background_classes ) ) { | |
| 158 | + if ( null !== self::$lazyload_background_classes ) { | |
| 156 | 159 | return self::$lazyload_background_classes; |
| 157 | 160 | } |
| 158 | 161 | |
| 159 | 162 | self::$lazyload_background_classes = apply_filters( 'optml_lazyload_bg_classes', [] ); |
| @@ -169,8 +172,9 @@ | ||
| 169 | 172 | |
| 170 | 173 | if ( self::$skip_lazyload_images !== null ) { |
| 171 | 174 | return self::$skip_lazyload_images; |
| 172 | 175 | } |
| 176 | + | |
| 173 | 177 | self::$skip_lazyload_images = apply_filters( 'optml_lazyload_images_skip', self::instance()->settings->get( 'skip_lazyload_images' ) ); |
| 174 | 178 | return self::$skip_lazyload_images; |
| 175 | 179 | } |
| 176 | 180 | /** |
| @@ -178,10 +182,9 @@ | ||
| 178 | 182 | * |
| 179 | 183 | * @return array |
| 180 | 184 | */ |
| 181 | 185 | public static function get_watcher_lz_classes() { |
| 182 | - | |
| 183 | - if ( ! empty( self::$lazyload_watcher_classes ) && is_array( self::$lazyload_watcher_classes ) ) { | |
| 186 | + if ( null !== self::$lazyload_watcher_classes ) { | |
| 184 | 187 | return self::$lazyload_watcher_classes; |
| 185 | 188 | } |
| 186 | 189 | |
| 187 | 190 | self::$lazyload_watcher_classes = apply_filters( 'optml_watcher_lz_classes', [] ); |
| @@ -197,24 +200,18 @@ | ||
| 197 | 200 | |
| 198 | 201 | if ( ! $this->settings->use_lazyload() ) { |
| 199 | 202 | return; |
| 200 | 203 | } |
| 201 | - if ( ! Optml_Filters::should_do_page( self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_URL ] ) ) { | |
| 204 | + $filters = $this->settings->get_filters(); | |
| 205 | + if ( ! Optml_Filters::should_do_page( $filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_URL ], $filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_URL_MATCH ] ) ) { | |
| 202 | 206 | return; |
| 203 | 207 | } |
| 204 | 208 | |
| 205 | - add_filter( | |
| 206 | - 'max_srcset_image_width', | |
| 207 | - function () { | |
| 208 | - return 1; | |
| 209 | - } | |
| 210 | - ); | |
| 211 | 209 | self::$is_lazyload_placeholder = self::$instance->settings->get( 'lazyload_placeholder' ) === 'enabled'; |
| 212 | 210 | |
| 213 | 211 | add_filter( 'optml_tag_replace', [ $this, 'lazyload_tag_replace' ], 2, 6 ); |
| 214 | 212 | |
| 215 | - add_filter( 'optml_video_replace', [$this, 'lazyload_video_replace'], 2, 1 ); | |
| 216 | - | |
| 213 | + add_filter( 'optml_video_replace', [ $this, 'lazyload_video_replace' ], 2, 1 ); | |
| 217 | 214 | } |
| 218 | 215 | /** |
| 219 | 216 | * Check if there are lazyloaded iframes. |
| 220 | 217 | * |
| @@ -224,8 +221,25 @@ | ||
| 224 | 221 | return self::$found_iframe; |
| 225 | 222 | } |
| 226 | 223 | |
| 227 | 224 | /** |
| 225 | + * Check if the img tag should be lazyloaded early. | |
| 226 | + * | |
| 227 | + * @param string $image_tag The image tag. | |
| 228 | + * @return bool Whether the image tag should be lazyloaded early or not. | |
| 229 | + */ | |
| 230 | + public function should_lazyload_early( $image_tag ) { | |
| 231 | + $flags = apply_filters( 'optml_lazyload_early_flags', [] ); | |
| 232 | + | |
| 233 | + foreach ( $flags as $flag ) { | |
| 234 | + if ( strpos( $image_tag, $flag ) !== false ) { | |
| 235 | + return true; | |
| 236 | + } | |
| 237 | + } | |
| 238 | + | |
| 239 | + return false; | |
| 240 | + } | |
| 241 | + /** | |
| 228 | 242 | * Replaces the tags with lazyload tags. |
| 229 | 243 | * |
| 230 | 244 | * @param string $new_tag The new tag. |
| 231 | 245 | * @param string $original_url The original URL. |
| @@ -238,16 +252,49 @@ | ||
| 238 | 252 | */ |
| 239 | 253 | public function lazyload_tag_replace( $new_tag, $original_url, $new_url, $optml_args, $is_slashed = false, $full_tag = '' ) { |
| 240 | 254 | |
| 241 | 255 | if ( ! $this->can_lazyload_for( $original_url, $full_tag ) ) { |
| 256 | + | |
| 242 | 257 | return Optml_Tag_Replacer::instance()->regular_tag_replace( $new_tag, $original_url, $new_url, $optml_args, $is_slashed ); |
| 243 | 258 | } |
| 259 | + // Remove fetchpriority high attribute when the image is lazyloaded | |
| 260 | + if ( strpos( $new_tag, 'fetchpriority' ) !== false ) { | |
| 261 | + $new_tag = str_ireplace( | |
| 262 | + [ | |
| 263 | + 'fetchpriority=high', | |
| 264 | + 'fetchpriority="high"', | |
| 265 | + "fetchpriority='high'", | |
| 266 | + 'fetchpriority=\"high\"', | |
| 267 | + "fetchpriority=\'high\'", | |
| 268 | + ], | |
| 269 | + '', | |
| 270 | + $new_tag | |
| 271 | + ); | |
| 272 | + } | |
| 273 | + | |
| 274 | + if ( self::instance()->settings->get( 'native_lazyload' ) === 'enabled' ) { | |
| 275 | + // First check if loading attribute already exists | |
| 276 | + if ( strpos( $new_tag, 'loading=' ) !== false ) { | |
| 277 | + // Replace existing loading attribute with lazy | |
| 278 | + $loading_attr = $is_slashed ? 'loading=\"lazy\"' : 'loading="lazy"'; | |
| 279 | + $new_tag = preg_replace( '/loading=(["\'])[^"\']*\1/i', $loading_attr, $new_tag ); | |
| 280 | + } else { | |
| 281 | + // No loading attribute exists, add it after <img | |
| 282 | + $loading_attr = $is_slashed ? ' loading=\"lazy\"' : ' loading="lazy"'; | |
| 283 | + $new_tag = preg_replace( '/<img/i', '<img' . $loading_attr, $new_tag, 1 ); | |
| 284 | + } | |
| 285 | + return $new_tag; | |
| 286 | + } | |
| 287 | + | |
| 288 | + // we remove the loading attribute if it exists with strpos | |
| 289 | + $new_tag = preg_replace( '/loading=(["\'])[^"\']*\1/i', '', $new_tag ); | |
| 290 | + | |
| 244 | 291 | $should_ignore_rescale = ! $this->is_valid_mimetype_from_url( $original_url, [ 'gif' => true, 'svg' => true ] ); |
| 245 | 292 | |
| 246 | 293 | if ( ! self::$is_lazyload_placeholder && ! $should_ignore_rescale ) { |
| 247 | 294 | $optml_args['quality'] = 'eco'; |
| 248 | 295 | $optml_args['resize'] = []; |
| 249 | - $low_url = apply_filters( 'optml_content_url', $original_url, $optml_args ); | |
| 296 | + $low_url = apply_filters( 'optml_content_url', $this->get_unoptimized_url( $original_url ), $optml_args ); | |
| 250 | 297 | $low_url = $is_slashed ? addcslashes( $low_url, '/' ) : $low_url; |
| 251 | 298 | } else { |
| 252 | 299 | $low_url = $this->get_svg_for( |
| 253 | 300 | isset( $optml_args['width'] ) ? $optml_args['width'] : '100%', |
| @@ -254,20 +301,28 @@ | ||
| 254 | 301 | isset( $optml_args['height'] ) ? $optml_args['height'] : '100%', |
| 255 | 302 | ( $should_ignore_rescale ? null : $original_url ) |
| 256 | 303 | ); |
| 257 | 304 | } |
| 258 | - | |
| 259 | 305 | $opt_format = ''; |
| 260 | 306 | |
| 261 | 307 | if ( $this->should_add_data_tag( $full_tag ) ) { |
| 262 | 308 | $opt_format = ' data-opt-src="%s" '; |
| 309 | + $custom_class = ''; | |
| 310 | + | |
| 263 | 311 | if ( $should_ignore_rescale ) { |
| 312 | + $custom_class .= 'optimole-lazy-only '; | |
| 313 | + } | |
| 314 | + if ( $this->should_lazyload_early( $full_tag ) ) { | |
| 315 | + $custom_class .= 'optimole-load-early'; | |
| 316 | + } | |
| 317 | + | |
| 318 | + if ( ! empty( $custom_class ) ) { | |
| 264 | 319 | if ( strpos( $new_tag, 'class=' ) === false ) { |
| 265 | - $opt_format .= ' class="optimole-lazy-only" '; | |
| 320 | + $opt_format .= ' class="' . $custom_class . '" '; | |
| 266 | 321 | } else { |
| 267 | 322 | $new_tag = str_replace( |
| 268 | 323 | ( $is_slashed ? 'class=\"' : 'class="' ), |
| 269 | - ( $is_slashed ? 'class=\"optimole-lazy-only ' : 'class="optimole-lazy-only ' ), | |
| 324 | + ( $is_slashed ? 'class=\"' . $custom_class . ' ' : 'class="' . $custom_class . ' ' ), | |
| 270 | 325 | $new_tag |
| 271 | 326 | ); |
| 272 | 327 | } |
| 273 | 328 | } |
| @@ -293,12 +348,16 @@ | ||
| 293 | 348 | ], |
| 294 | 349 | $new_tag, |
| 295 | 350 | 1 |
| 296 | 351 | ); |
| 352 | + | |
| 353 | + // We remove srcset and sizes attributes since our lazyload does not need them. | |
| 354 | + $pattern = '/\s+(?:srcset|sizes)=("[^"]*"|\'[^\']*\'|\\\\"[^\\\\"]*\\\\")/i'; | |
| 355 | + $new_tag = preg_replace( $pattern, '', $new_tag ); | |
| 356 | + | |
| 357 | + // We keep this for srcset lazyload compatibility that might break our mechanism. | |
| 297 | 358 | $new_tag = str_replace( 'srcset=', 'old-srcset=', $new_tag ); |
| 298 | - if ( strpos( $new_tag, 'loading=' ) === false && self::instance()->settings->get( 'native_lazyload' ) === 'enabled' ) { | |
| 299 | - $new_tag = preg_replace( '/<img/im', $is_slashed ? '<img loading=\"lazy\"' : '<img loading="lazy"', $new_tag ); | |
| 300 | - } | |
| 359 | + | |
| 301 | 360 | if ( ! $this->should_add_noscript( $new_tag ) ) { |
| 302 | 361 | return $new_tag; |
| 303 | 362 | } |
| 304 | 363 | |
| @@ -315,14 +374,16 @@ | ||
| 315 | 374 | $video_tags = []; |
| 316 | 375 | $iframes = []; |
| 317 | 376 | $videos = []; |
| 318 | 377 | preg_match_all( '#(?:<noscript\s*>\s*)?<iframe(.*?)></iframe>(?:\s*</noscript\s*>)?#is', $content, $iframes ); |
| 319 | - preg_match_all( '#(?:<noscript\s*>\s*)?<video(.*?)video>(?:\s*</noscript\s*>)?#is', $content, $videos ); | |
| 378 | + preg_match_all( '#(?:<noscript\s*>\s*)?<video(.*?)>.*?</video>(?:\s*</noscript\s*>)?#is', $content, $videos ); | |
| 320 | 379 | $video_tags = array_merge( $iframes[0], $videos[0] ); |
| 321 | 380 | |
| 322 | 381 | $search = []; |
| 323 | 382 | $replace = []; |
| 383 | + | |
| 324 | 384 | foreach ( $video_tags as $video_tag ) { |
| 385 | + | |
| 325 | 386 | if ( ! $this->should_lazyload_iframe( $video_tag ) ) { |
| 326 | 387 | continue; |
| 327 | 388 | } |
| 328 | 389 | if ( preg_match( "/ data-opt-src=['\"]/is", $video_tag ) ) { |
| @@ -327,20 +388,29 @@ | ||
| 327 | 388 | } |
| 328 | 389 | if ( preg_match( "/ data-opt-src=['\"]/is", $video_tag ) ) { |
| 329 | 390 | continue; |
| 330 | 391 | } |
| 331 | - array_push( $search, $video_tag ); | |
| 332 | - $no_script = $video_tag; | |
| 392 | + $should_add_noscript = true; | |
| 393 | + $original_tag = $video_tag; | |
| 333 | 394 | // replace the src and add the data-opt-src attribute |
| 334 | 395 | if ( strpos( $video_tag, 'iframe' ) !== false ) { |
| 335 | 396 | $video_tag = preg_replace( '/iframe(.*?)src=/is', 'iframe$1 src="about:blank" data-opt-src=', $video_tag ); |
| 336 | 397 | } elseif ( strpos( $video_tag, 'video' ) !== false ) { |
| 337 | - $video_tag = preg_replace( '/video(.*?)src=/is', 'video$1 data-opt-src=', $video_tag ); | |
| 398 | + if ( strpos( $video_tag, 'source' ) !== false ) { | |
| 399 | + if ( strpos( $video_tag, 'preload' ) === false ) { | |
| 400 | + $video_tag = preg_replace( '/video(.*?)>/is', 'video$1 preload="metadata">', $video_tag, 1 ); | |
| 401 | + $should_add_noscript = false; | |
| 402 | + } else { | |
| 403 | + continue; | |
| 404 | + } | |
| 405 | + } else { | |
| 406 | + $video_tag = preg_replace( '/video(.*?)src=/is', 'video$1 data-opt-src=', $video_tag ); | |
| 407 | + } | |
| 338 | 408 | } |
| 339 | - | |
| 340 | - if ( $this->should_add_noscript( $video_tag ) ) { | |
| 341 | - $video_tag .= '<noscript>' . $no_script . '</noscript>'; | |
| 409 | + if ( $this->should_add_noscript( $video_tag ) && $should_add_noscript ) { | |
| 410 | + $video_tag .= '<noscript>' . $original_tag . '</noscript>'; | |
| 342 | 411 | } |
| 412 | + array_push( $search, $original_tag ); | |
| 343 | 413 | array_push( $replace, $video_tag ); |
| 344 | 414 | self::$found_iframe = true; |
| 345 | 415 | } |
| 346 | 416 | $search = array_unique( $search ); |
| @@ -357,8 +427,11 @@ | ||
| 357 | 427 | * |
| 358 | 428 | * @return bool We can lazyload? |
| 359 | 429 | */ |
| 360 | 430 | public function can_lazyload_for( $url, $tag = '' ) { |
| 431 | + if ( OPTML_DEBUG ) { | |
| 432 | + do_action( 'optml_log', 'can_lazyload_for: ' . $url . ' ' . $tag ); | |
| 433 | + } | |
| 361 | 434 | foreach ( self::possible_lazyload_flags() as $banned_string ) { |
| 362 | 435 | if ( strpos( $tag, $banned_string ) !== false ) { |
| 363 | 436 | return false; |
| 364 | 437 | } |
| @@ -372,9 +445,9 @@ | ||
| 372 | 445 | basename( $url ), |
| 373 | 446 | Optml_Config::$image_extensions |
| 374 | 447 | ); |
| 375 | 448 | |
| 376 | - if ( ! isset( $type['ext'] ) || empty( $type['ext'] ) ) { | |
| 449 | + if ( empty( $type['ext'] ) ) { | |
| 377 | 450 | return false; |
| 378 | 451 | } |
| 379 | 452 | |
| 380 | 453 | if ( false === Optml_Filters::should_do_extension( self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_EXT ], $type['ext'] ) ) { |
| @@ -383,9 +456,35 @@ | ||
| 383 | 456 | |
| 384 | 457 | if ( defined( 'OPTML_DISABLE_PNG_LAZYLOAD' ) && OPTML_DISABLE_PNG_LAZYLOAD ) { |
| 385 | 458 | return $type['ext'] !== 'png'; |
| 386 | 459 | } |
| 387 | - if ( Optml_Tag_Replacer::$lazyload_skipped_images < self::get_skip_lazyload_limit() ) { | |
| 460 | + | |
| 461 | + $no_viewport_data_available = true; | |
| 462 | + | |
| 463 | + if ( $this->settings->is_lazyload_type_viewport() ) { | |
| 464 | + $image_id = $this->get_id_by_url( $url ); | |
| 465 | + $is_in_all_viewports = Optml_Manager::instance()->page_profiler->is_in_all_viewports( $image_id ); | |
| 466 | + $is_lcp_image = Optml_Manager::instance()->page_profiler->is_lcp_image_in_all_viewports( $image_id ); | |
| 467 | + $no_viewport_data_available = ! Optml_Manager::instance()->page_profiler->is_data_available(); | |
| 468 | + | |
| 469 | + if ( OPTML_DEBUG ) { | |
| 470 | + if ( $is_in_all_viewports ) { | |
| 471 | + do_action( 'optml_log', 'Lazyload skipped image is in all viewports ' . $url . '|' . $image_id ); | |
| 472 | + } elseif ( $is_lcp_image ) { | |
| 473 | + do_action( 'optml_log', 'Lazyload skipped image is LCP ' . $url . '|' . $image_id ); | |
| 474 | + } | |
| 475 | + } | |
| 476 | + | |
| 477 | + if ( $is_in_all_viewports || $is_lcp_image ) { | |
| 478 | + return false; | |
| 479 | + } | |
| 480 | + } | |
| 481 | + | |
| 482 | + if ( | |
| 483 | + $no_viewport_data_available && | |
| 484 | + $this->settings->is_lazyload_type_fixed() && | |
| 485 | + Optml_Tag_Replacer::$lazyload_skipped_images < self::get_skip_lazyload_limit() | |
| 486 | + ) { | |
| 388 | 487 | return false; |
| 389 | 488 | } |
| 390 | 489 | return true; |
| 391 | 490 | } |
| @@ -409,10 +508,10 @@ | ||
| 409 | 508 | |
| 410 | 509 | /** |
| 411 | 510 | * Get SVG markup with specific width/height. |
| 412 | 511 | * |
| 413 | - * @param int $width Markup Width. | |
| 414 | - * @param int $height Markup Height. | |
| 512 | + * @param int|string $width Markup Width. | |
| 513 | + * @param int|string $height Markup Height. | |
| 415 | 514 | * @param string|null $url Original URL. |
| 416 | 515 | * |
| 417 | 516 | * @return string SVG code. |
| 418 | 517 | */ |
| @@ -429,23 +528,39 @@ | ||
| 429 | 528 | $sizes = getimagesize( $filepath ); |
| 430 | 529 | wp_cache_add( $key, [ $sizes[0], $sizes[1] ], 'optml_sources', DAY_IN_SECONDS ); |
| 431 | 530 | } |
| 432 | 531 | } |
| 433 | - list( $width, $height ) = $sizes; | |
| 532 | + // Check if $sizes is an array before list() assignment to prevent fatal error on PHP 8.2+ | |
| 533 | + // when the image file is missing (e.g., offloaded to CDN or deleted). | |
| 534 | + if ( is_array( $sizes ) ) { | |
| 535 | + list( $width, $height ) = $sizes; | |
| 536 | + } | |
| 434 | 537 | } |
| 538 | + // If the width is not found the url might be an offloaded attachment so we can get the width and height from the metadata. | |
| 539 | + if ( ! is_numeric( $width ) && ! empty( $url ) ) { | |
| 540 | + $attachment_id = $this->attachment_url_to_post_id( $url ); | |
| 541 | + $meta = wp_get_attachment_metadata( $attachment_id ); | |
| 542 | + $width = $meta['width'] ?? false; | |
| 543 | + $height = $meta['height'] ?? false; | |
| 544 | + } | |
| 435 | 545 | |
| 436 | 546 | $width = ! is_numeric( $width ) ? '100%' : $width; |
| 437 | 547 | $height = ! is_numeric( $height ) ? '100%' : $height; |
| 548 | + $fill = $this->settings->get( 'placeholder_color' ); | |
| 438 | 549 | |
| 439 | - return | |
| 440 | - str_replace( | |
| 441 | - [ '#width#', '#height#' ], | |
| 442 | - [ | |
| 443 | - $width, | |
| 444 | - $height, | |
| 445 | - ], | |
| 446 | - self::SVG_PLACEHOLDER | |
| 447 | - ); | |
| 550 | + if ( empty( $fill ) ) { | |
| 551 | + $fill = 'transparent'; | |
| 552 | + } | |
| 553 | + | |
| 554 | + return str_replace( | |
| 555 | + [ '#width#', '#height#', '#fill#' ], | |
| 556 | + [ | |
| 557 | + $width, | |
| 558 | + $height, | |
| 559 | + urlencode( $fill ), | |
| 560 | + ], | |
| 561 | + self::SVG_PLACEHOLDER | |
| 562 | + ); | |
| 448 | 563 | } |
| 449 | 564 | |
| 450 | 565 | /** |
| 451 | 566 | * Check if we should add the noscript tag. |
| @@ -454,8 +569,11 @@ | ||
| 454 | 569 | * |
| 455 | 570 | * @return bool Should add? |
| 456 | 571 | */ |
| 457 | 572 | public function should_add_noscript( $tag ) { |
| 573 | + if ( $this->settings->get( 'no_script' ) === 'disabled' ) { | |
| 574 | + return false; | |
| 575 | + } | |
| 458 | 576 | foreach ( self::get_ignore_noscript_flags() as $banned_string ) { |
| 459 | 577 | if ( strpos( $tag, $banned_string ) !== false ) { |
| 460 | 578 | return false; |
| 461 | 579 | } |
| @@ -470,8 +588,9 @@ | ||
| 470 | 588 | * |
| 471 | 589 | * @return bool Should add? |
| 472 | 590 | */ |
| 473 | 591 | public function should_lazyload_iframe( $tag ) { |
| 592 | + | |
| 474 | 593 | foreach ( self::get_iframe_lazyload_flags() as $banned_string ) { |
| 475 | 594 | if ( strpos( $tag, $banned_string ) !== false ) { |
| 476 | 595 | return false; |
| 477 | 596 | } |
| @@ -485,14 +604,13 @@ | ||
| 485 | 604 | * |
| 486 | 605 | * @return array |
| 487 | 606 | */ |
| 488 | 607 | public static function get_ignore_noscript_flags() { |
| 489 | - | |
| 490 | - if ( ! empty( self::$ignore_no_script_flags ) && is_array( self::$ignore_no_script_flags ) ) { | |
| 608 | + if ( null !== self::$ignore_no_script_flags ) { | |
| 491 | 609 | return self::$ignore_no_script_flags; |
| 492 | 610 | } |
| 493 | 611 | |
| 494 | - self::$ignore_no_script_flags = apply_filters( 'optml_ignore_noscript_on', [] ); | |
| 612 | + self::$ignore_no_script_flags = apply_filters( 'optml_ignore_noscript_on', [ '<noscript' ] ); | |
| 495 | 613 | |
| 496 | 614 | return self::$ignore_no_script_flags; |
| 497 | 615 | } |
| 498 | 616 | /** |
| @@ -500,14 +618,14 @@ | ||
| 500 | 618 | * |
| 501 | 619 | * @return array |
| 502 | 620 | */ |
| 503 | 621 | public static function get_iframe_lazyload_flags() { |
| 504 | - | |
| 505 | - if ( ! empty( self::$iframe_lazyload_flags ) && is_array( self::$iframe_lazyload_flags ) ) { | |
| 622 | + if ( null !== self::$iframe_lazyload_flags ) { | |
| 506 | 623 | return self::$iframe_lazyload_flags; |
| 507 | 624 | } |
| 508 | 625 | |
| 509 | - self::$iframe_lazyload_flags = apply_filters( 'optml_iframe_lazyload_flags', [ 'gform_ajax_frame', '<noscript', 'recaptcha', 'original-src' ] ); | |
| 626 | + self::$iframe_lazyload_flags = self::possible_lazyload_flags(); | |
| 627 | + self::$iframe_lazyload_flags = array_merge( self::$iframe_lazyload_flags, apply_filters( 'optml_iframe_lazyload_flags', [ 'gform_ajax_frame', '<noscript', 'recaptcha', '-src' ] ) ); | |
| 510 | 628 | |
| 511 | 629 | return self::$iframe_lazyload_flags; |
| 512 | 630 | } |
| 513 | 631 | /** |
| @@ -537,6 +655,5 @@ | ||
| 537 | 655 | public function __wakeup() { |
| 538 | 656 | // Unserializing instances of the class is forbidden. |
| 539 | 657 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'optimole-wp' ), '1.0.0' ); |
| 540 | 658 | } |
| 541 | - | |
| 542 | 659 | } |