#isU', $content, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$attrs = Utility::parse_attr($match[1]);
if ( empty( $attrs['src'] ) ) {
continue;
}
if ( false !== strpos( $attrs['src'], 'base64' ) || 0 === strpos( $attrs['src'], 'data:' ) ) {
self::debug2( 'lazyload bypassed base64 img' );
continue;
}
if ( false !== strpos( $attrs['src'], '{' ) ) {
self::debug2( 'image src has {} ' . $attrs['src'] );
continue;
}
// If the src contains VPI filename, then preload it.
if ( ! Utility::str_hit_array( $attrs['src'], $vpi_files ) ) {
continue;
}
self::debug2( 'VPI preload found and matched: ' . $attrs['src'] );
$this->_vpi_preload_list[] = $attrs['src'];
// Add attributes fetchpriority="high" and decode="sync"
// after WP 6.3.0 use: wp_img_tag_add_loading_optimization_attrs().
$new_html = [];
$attrs[ 'fetchpriority' ] = 'high';
$attrs[ 'decoding' ] = 'sync';
// create html with new attributes.
foreach ( $attrs as $k => $attr ) {
$new_html[] = $k . '="' . $attr . '"';
}
if ( $new_html ) {
$vpi_fp_search[] = $match[1];
$vpi_fp_replace[] = implode( ' ', $new_html);
}
}
// if VPI fetchpriority changes, do the replacement
if ( $vpi_fp_search && $vpi_fp_replace ) {
$this->content = str_replace( $vpi_fp_search, $vpi_fp_replace, $this->content );
}
unset( $vpi_fp_search );
unset( $vpi_fp_replace );
}
/**
* Parse img src.
*
* @since 1.4
* @access private
* @return array{0:array,1:array,2:array} All the src & related raw html list with placeholders.
*/
private function _parse_img() {
/**
* Exclude list.
*
* @since 1.5
* @since 2.7.1 Changed to array.
*/
$excludes = apply_filters( 'litespeed_media_lazy_img_excludes', $this->conf( Base::O_MEDIA_LAZY_EXC ) );
$cls_excludes = apply_filters( 'litespeed_media_lazy_img_cls_excludes', $this->conf( Base::O_MEDIA_LAZY_CLS_EXC ) );
$cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427
$src_list = [];
$html_list = [];
$placeholder_list = [];
$content = preg_replace(
[
'##sU',
'##isU',
'##isU', // Remove script to avoid false matches and warnings, when image size detection is turned ON.
],
'',
$this->content
);
/**
* Exclude parent classes.
*
* @since 3.0
*/
$parent_cls_exc = apply_filters( 'litespeed_media_lazy_img_parent_cls_excludes', $this->conf( Base::O_MEDIA_LAZY_PARENT_CLS_EXC ) );
if ( $parent_cls_exc ) {
self::debug2( 'Lazyload Class excludes', $parent_cls_exc );
foreach ( $parent_cls_exc as $v ) {
$content = preg_replace('#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote($v, '#') . '[^\'"]*\2[^>]*>.*\1>#sU', '', $content);
}
}
$add_missing_sizes = ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_MEDIA_ADD_MISSING_SIZES ) )
&& apply_filters( 'litespeed_media_add_missing_sizes', true );
preg_match_all( '#
]+)/?>#isU', $content, $matches, PREG_SET_ORDER );
foreach ( $matches as $match ) {
$attrs = Utility::parse_attr( $match[1] );
if ( empty( $attrs['src'] ) ) {
continue;
}
/**
* Add src validation to bypass base64 img src.
*
* @since 1.6
*/
if ( false !== strpos( $attrs['src'], 'base64' ) || 0 === strpos( $attrs['src'], 'data:' ) ) {
self::debug2( 'lazyload bypassed base64 img' );
continue;
}
self::debug2( 'lazyload found: ' . $attrs['src'] );
if (
! empty( $attrs['data-no-lazy'] ) ||
! empty( $attrs['data-skip-lazy'] ) ||
! empty( $attrs['data-lazyloaded'] ) ||
! empty( $attrs['data-src'] ) ||
! empty( $attrs['data-srcset'] )
) {
self::debug2( 'bypassed' );
continue;
}
$hit = ! empty( $attrs['class'] ) ? Utility::str_hit_array( $attrs['class'], $cls_excludes ) : false;
if ( $hit ) {
self::debug2( 'lazyload image cls excludes [hit] ' . $hit );
continue;
}
/**
* Exclude from lazyload by setting.
*
* @since 1.5
*/
if ( $excludes && Utility::str_hit_array( $attrs['src'], $excludes ) ) {
self::debug2( 'lazyload image exclude ' . $attrs['src'] );
continue;
}
/**
* Excludes invalid image src from buddypress avatar crop.
*
* @see https://wordpress.org/support/topic/lazy-load-breaking-buddypress-upload-avatar-feature
* @since 3.0
*/
if ( false !== strpos( $attrs['src'], '{' ) ) {
self::debug2( 'image src has {} ' . $attrs['src'] );
continue;
}
// to avoid multiple replacement.
if ( in_array( $match[0], $html_list, true ) ) {
continue;
}
// Add missing dimensions.
if ( $add_missing_sizes ) {
if ( empty( $attrs['width'] ) || 'auto' === $attrs['width'] || empty( $attrs['height'] ) || 'auto' === $attrs['height'] ) {
self::debug( '⚠️ Missing sizes for image [src] ' . $attrs['src'] );
$dimensions = $this->_detect_dimensions( $attrs['src'] );
if ( $dimensions ) {
$ori_width = $dimensions[0];
$ori_height = $dimensions[1];
// Calculate height based on width.
if ( ! empty( $attrs['width'] ) && 'auto' !== $attrs['width'] ) {
$ori_height = (int) ( ( $ori_height * (int) $attrs['width'] ) / max( 1, $ori_width ) );
} elseif ( ! empty( $attrs['height'] ) && 'auto' !== $attrs['height'] ) {
$ori_width = (int) ( ( $ori_width * (int) $attrs['height'] ) / max( 1, $ori_height ) );
}
// Remove existing width/height, then append new values
$inner = Utility::remove_attr( $match[1], 'width' );
$inner = Utility::remove_attr( $inner, 'height' );
$new_html = '
';
self::debug( 'Add missing sizes ' . $ori_width . 'x' . $ori_height . ' to ' . $attrs['src'] );
$this->content = str_replace( $match[0], $new_html, $this->content );
$match[0] = $new_html;
$attrs['width'] = $ori_width;
$attrs['height'] = $ori_height;
}
}
}
$placeholder = false;
if ( ! empty( $attrs['width'] ) && 'auto' !== $attrs['width'] && ! empty( $attrs['height'] ) && 'auto' !== $attrs['height'] ) {
$placeholder = (int) $attrs['width'] . 'x' . (int) $attrs['height'];
}
$src_list[] = $attrs['src'];
$html_list[] = $match[0];
$placeholder_list[] = $placeholder;
}
return [ $src_list, $html_list, $placeholder_list ];
}
/**
* Detect the original sizes.
*
* @since 4.0
*
* @param string $src Source URL/path.
* @return array|false getimagesize array or false.
*/
private function _detect_dimensions( $src ) {
$pathinfo = Utility::is_internal_file( $src );
if ( $pathinfo ) {
$src = $pathinfo[0];
} elseif ( apply_filters( 'litespeed_media_ignore_remote_missing_sizes', false ) ) {
return false;
}
if ( 0 === strpos( $src, '//' ) ) {
$src = 'https:' . $src;
}
try {
$sizes = getimagesize( $src );
} catch ( \Exception $e ) {
return false;
}
if ( ! empty( $sizes[0] ) && ! empty( $sizes[1] ) ) {
return $sizes;
}
return false;
}
/**
* Parse iframe src.
*
* @since 1.4
* @access private
* @return array All the related raw html list (full