', 'is_valid_mimetype_from_url( $original_url, [ 'gif' => true, 'svg' => true ] ); if ( ! self::$is_lazyload_placeholder && ! $should_ignore_rescale ) { $optml_args['quality'] = 'eco'; $optml_args['resize'] = []; $low_url = apply_filters( 'optml_content_url', $this->get_unoptimized_url( $original_url ), $optml_args ); $low_url = $is_slashed ? addcslashes( $low_url, '/' ) : $low_url; } else { $low_url = $this->get_svg_for( isset( $optml_args['width'] ) ? $optml_args['width'] : '100%', isset( $optml_args['height'] ) ? $optml_args['height'] : '100%', ( $should_ignore_rescale ? null : $original_url ) ); } $opt_format = ''; if ( $this->should_add_data_tag( $full_tag ) ) { $opt_format = ' data-opt-src="%s" '; $custom_class = ''; if ( $should_ignore_rescale ) { $custom_class .= 'optimole-lazy-only '; } if ( $this->should_lazyload_early( $full_tag ) ) { $custom_class .= 'optimole-load-early'; } if ( ! empty( $custom_class ) ) { if ( strpos( $new_tag, 'class=' ) === false ) { $opt_format .= ' class="' . $custom_class . '" '; } else { $new_tag = str_replace( ( $is_slashed ? 'class=\"' : 'class="' ), ( $is_slashed ? 'class=\"' . $custom_class . ' ' : 'class="' . $custom_class . ' ' ), $new_tag ); } } $opt_format = $is_slashed ? addslashes( $opt_format ) : $opt_format; } $new_url = $is_slashed ? addcslashes( $new_url, '/' ) : $new_url; $opt_src = sprintf( $opt_format, $new_url ); $no_script_tag = str_replace( $original_url, $new_url, $new_tag ); $new_tag = preg_replace( [ '/((?:\s|\'|"){1,}src(?>=|"|\'|\s|\\\\)*)' . preg_quote( $original_url, '/' ) . '/m', '/' . $no_script_tag . ''; } /** * Replaces video embeds with lazyload embeds. * * @param string $content Html page content. * * @return string */ public function lazyload_video_replace( $content ) { $video_tags = []; $iframes = []; $videos = []; preg_match_all( '#(?:\s*)?(?:\s*)?#is', $content, $iframes ); preg_match_all( '#(?:\s*)?.*?(?:\s*)?#is', $content, $videos ); $video_tags = array_merge( $iframes[0], $videos[0] ); $search = []; $replace = []; foreach ( $video_tags as $video_tag ) { if ( ! $this->should_lazyload_iframe( $video_tag ) ) { continue; } if ( preg_match( "/ data-opt-src=['\"]/is", $video_tag ) ) { continue; } $should_add_noscript = true; $original_tag = $video_tag; // replace the src and add the data-opt-src attribute if ( strpos( $video_tag, 'iframe' ) !== false ) { $video_tag = preg_replace( '/iframe(.*?)src=/is', 'iframe$1 src="about:blank" data-opt-src=', $video_tag ); } elseif ( strpos( $video_tag, 'video' ) !== false ) { if ( strpos( $video_tag, 'source' ) !== false ) { if ( strpos( $video_tag, 'preload' ) === false ) { $video_tag = preg_replace( '/video(.*?)>/is', 'video$1 preload="metadata">', $video_tag, 1 ); $should_add_noscript = false; } else { continue; } } else { $video_tag = preg_replace( '/video(.*?)src=/is', 'video$1 data-opt-src=', $video_tag ); } } if ( $this->should_add_noscript( $video_tag ) && $should_add_noscript ) { $video_tag .= ''; } array_push( $search, $original_tag ); array_push( $replace, $video_tag ); self::$found_iframe = true; } $search = array_unique( $search ); $replace = array_unique( $replace ); $content = str_replace( $search, $replace, $content ); return $content; } /** * Check if the lazyload is allowed for this url. * * @param string $url Url. * @param string $tag Html tag. * * @return bool We can lazyload? */ public function can_lazyload_for( $url, $tag = '' ) { if ( OPTML_DEBUG ) { do_action( 'optml_log', 'can_lazyload_for: ' . $url . ' ' . $tag ); } foreach ( self::possible_lazyload_flags() as $banned_string ) { if ( strpos( $tag, $banned_string ) !== false ) { return false; } } if ( false === Optml_Filters::should_do_image( $url, self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_FILENAME ] ) ) { return false; } $url = strtok( $url, '?' ); $type = wp_check_filetype( basename( $url ), Optml_Config::$image_extensions ); if ( empty( $type['ext'] ) ) { return false; } if ( false === Optml_Filters::should_do_extension( self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_EXT ], $type['ext'] ) ) { return false; } if ( defined( 'OPTML_DISABLE_PNG_LAZYLOAD' ) && OPTML_DISABLE_PNG_LAZYLOAD ) { return $type['ext'] !== 'png'; } $no_viewport_data_available = true; if ( $this->settings->is_lazyload_type_viewport() ) { $image_id = $this->get_id_by_url( $url ); $is_in_all_viewports = Optml_Manager::instance()->page_profiler->is_in_all_viewports( $image_id ); $is_lcp_image = Optml_Manager::instance()->page_profiler->is_lcp_image_in_all_viewports( $image_id ); $no_viewport_data_available = ! Optml_Manager::instance()->page_profiler->is_data_available(); if ( OPTML_DEBUG ) { if ( $is_in_all_viewports ) { do_action( 'optml_log', 'Lazyload skipped image is in all viewports ' . $url . '|' . $image_id ); } elseif ( $is_lcp_image ) { do_action( 'optml_log', 'Lazyload skipped image is LCP ' . $url . '|' . $image_id ); } } if ( $is_in_all_viewports || $is_lcp_image ) { return false; } } if ( $no_viewport_data_available && $this->settings->is_lazyload_type_fixed() && Optml_Tag_Replacer::$lazyload_skipped_images < self::get_skip_lazyload_limit() ) { return false; } return true; } /** * Check if we should add the data-opt-tag. * * @param string $tag Html tag. * * @return bool Should add? */ public function should_add_data_tag( $tag ) { foreach ( self::possible_data_ignore_flags() as $banned_string ) { if ( strpos( $tag, $banned_string ) !== false ) { return false; } } return true; } /** * Get SVG markup with specific width/height. * * @param int|string $width Markup Width. * @param int|string $height Markup Height. * @param string|null $url Original URL. * * @return string SVG code. */ public function get_svg_for( $width, $height, $url = null ) { if ( $url !== null && ! is_numeric( $width ) ) { $url = strtok( $url, '?' ); $key = crc32( $url ); $sizes = wp_cache_get( $key, 'optml_sources' ); if ( $sizes === false ) { $filepath = substr( $url, strpos( $url, $this->upload_resource['content_folder'] ) + $this->upload_resource['content_folder_length'] ); $filepath = WP_CONTENT_DIR . $filepath; if ( is_file( $filepath ) ) { $sizes = getimagesize( $filepath ); wp_cache_add( $key, [ $sizes[0], $sizes[1] ], 'optml_sources', DAY_IN_SECONDS ); } } // Check if $sizes is an array before list() assignment to prevent fatal error on PHP 8.2+ // when the image file is missing (e.g., offloaded to CDN or deleted). if ( is_array( $sizes ) ) { list( $width, $height ) = $sizes; } } // If the width is not found the url might be an offloaded attachment so we can get the width and height from the metadata. if ( ! is_numeric( $width ) && ! empty( $url ) ) { $attachment_id = $this->attachment_url_to_post_id( $url ); $meta = wp_get_attachment_metadata( $attachment_id ); $width = $meta['width'] ?? false; $height = $meta['height'] ?? false; } $width = ! is_numeric( $width ) ? '100%' : $width; $height = ! is_numeric( $height ) ? '100%' : $height; $fill = $this->settings->get( 'placeholder_color' ); if ( empty( $fill ) ) { $fill = 'transparent'; } return str_replace( [ '#width#', '#height#', '#fill#' ], [ $width, $height, urlencode( $fill ), ], self::SVG_PLACEHOLDER ); } /** * Check if we should add the noscript tag. * * @param string $tag Html tag. * * @return bool Should add? */ public function should_add_noscript( $tag ) { if ( $this->settings->get( 'no_script' ) === 'disabled' ) { return false; } foreach ( self::get_ignore_noscript_flags() as $banned_string ) { if ( strpos( $tag, $banned_string ) !== false ) { return false; } } return true; } /** * Check if we should lazyload iframe. * * @param string $tag Html tag. * * @return bool Should add? */ public function should_lazyload_iframe( $tag ) { foreach ( self::get_iframe_lazyload_flags() as $banned_string ) { if ( strpos( $tag, $banned_string ) !== false ) { return false; } } return true; } /** * Returns flags for ignoring noscript tag additional watch. * * @return array */ public static function get_ignore_noscript_flags() { if ( null !== self::$ignore_no_script_flags ) { return self::$ignore_no_script_flags; } self::$ignore_no_script_flags = apply_filters( 'optml_ignore_noscript_on', [ '