Skip to content

Commit

Permalink
Make callback filter a full utility filter and pass full img HTML
Browse files Browse the repository at this point in the history
Now that we're no longer using a `preg_replace_callback` function in
`tevkori_filter_content_images()`, it doesn't make as much sense to use
the callback function the way we originally set it up. However, rather
that merging the two function, this turns the callback into a useful
utility function that accepts an `img` element and returns the HTML
including `srcset` and `sizes` attributes if we can.

Additionally, since we only add `srcset` and `sizes` when we're able to
parse the attachment id from the markup, we also no longer need to limit
our matches in the content filter by files containing the path to our
uploads folder. This resolves #190.
  • Loading branch information
joemcgill committed Oct 4, 2015
1 parent 1643e63 commit 3ded293
Showing 1 changed file with 54 additions and 84 deletions.
138 changes: 54 additions & 84 deletions wp-tevko-responsive-images.php
Original file line number Diff line number Diff line change
Expand Up @@ -294,119 +294,86 @@ function tevkori_get_srcset_string( $id, $size = 'medium' ) {
}

/**
* Filters images in post content to add 'srcset' and 'sizes'.
*
* @since 2.5.0
*
* @param string $content The raw post content to be filtered.
* @return string Converted content with 'srcset' and 'sizes' added to images.
*/
* Filters images in post content to add 'srcset' and 'sizes'.
*
* @since 2.5.0
*
* @param string $content The raw post content to be filtered.
* @return string Converted content with 'srcset' and 'sizes' added to images.
*/
function tevkori_filter_content_images( $content ) {

// Only match images in our uploads directory.
$uploads_dir = wp_upload_dir();
$path_to_upload_dir = $uploads_dir['baseurl'];

// Pattern for matching all images with a `src` from the uploads directory.
$pattern = '|<img ([^>]+' . preg_quote( $path_to_upload_dir ) . '[^>]+)>|i';
preg_match_all( $pattern, $content, $matches );
if ( ! preg_match_all( '/<img [^>]+ wp-image-([0-9]+)[^>]+>/i', $content, $matches ) ) {
return $content;
}

$images = $matches[0];
$ids = array();

foreach( $images as $image ) {
if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) {
(int) $id = $class_id[1];
if ( $id ) {
$ids[] = $id;
}
}
}
$attachment_ids = array_unique( $matches[1] );

if ( 0 < count( $ids ) ) {
if ( 0 < count( $attachment_ids ) ) {
/*
* Warm object caches for use with wp_get_attachment_metadata.
*
* To avoid making a database call for each image, a single query
* warms the object cache with the meta information for all images.
**/
_prime_post_caches( $ids, false, true );
*/
_prime_post_caches( $attachment_ids, false, true );
}

foreach( $matches[0] as $k => $image ) {
$match = array( $image, $matches[1][$k] );
$needle = $image;
$replacement = _tevkori_filter_content_images_callback( $match );
if ( false === $replacement ) {
continue;
}
$content = str_replace( $image, $replacement, $content );
foreach( $images as $image ) {
$content = str_replace( $image, tevkori_img_add_srcset_and_sizes( $image ), $content );
}

return $content;
}
add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 );

/**
* Private preg_replace callback used in tevkori_filter_content_images()
*
* @access private
* @since 2.5.0
*/
function _tevkori_filter_content_images_callback( $image ) {
if ( empty( $image ) ) {
return false;
}

list( $image_html, $atts ) = $image;

// Bail early if a 'srcset' attribute already exists.
if ( false !== strpos( $atts, 'srcset=' ) ) {

/*
* Backward compatibility.
*
* Prior to version 2.5 a 'srcset' and 'data-sizes' attribute
* were added to the image while inserting the image in the content.
* We replace the 'data-sizes' attribute by a 'sizes' attribute.
*/
$image_html = str_replace( ' data-sizes="', ' sizes="', $image_html );

return $image_html;
* Add srcset and sizes to an 'img' element.
*
* @since 4.4.0
*
* @param string $image An HTML 'img' element to be filtered.
* @return string Converted 'img' element with 'srcset' and 'sizes' added.
*/
function tevkori_img_add_srcset_and_sizes( $image ) {
// Return early if a 'srcset' attribute already exists.
if ( false !== strpos( $image, ' srcset="' ) ) {
return $image;
}

// Grab ID and size info from core classes.
$id = preg_match( '/wp-image-([0-9]+)/i', $atts, $class_id ) ? (int) $class_id[1] : false;
$size = preg_match( '/size-([^\s|"]+)/i', $atts, $class_size ) ? $class_size[1] : false;
$width = preg_match( '/ width="([0-9]+)"/', $atts, $atts_width ) ? (int) $atts_width[1] : false;
$height = preg_match( '/ height="([0-9]+)"/', $atts, $atts_height ) ? (int) $atts_height[1] : false;
// Parse id, size, width, and height from the `img` element.
$id = preg_match( '/wp-image-([0-9]+)/i', $image, $match_id ) ? (int) $match_id[1] : false;
$size = preg_match( '/size-([^\s|"]+)/i', $image, $match_size ) ? $match_size[1] : false;
$width = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : false;
$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : false;

if ( $id && false === $size ) {
$size = array(
$width,
$height
$height,
);
}

/*
* If attempts to parse the size value failed, attempt to use the image
* metadata to match the 'src' angainst the available sizes for an attachment.
* metadata to match the 'src' against the available sizes for an attachment.
*/
if ( ! $size && ! empty( $id ) && $meta = wp_get_attachment_metadata( $id ) ) {
// Parse the image `src` value from the `img` element.
$src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false;

preg_match( '/src="([^"]+)"/', $atts, $url );

// Sanity check the 'src' value and bail early it doesn't exist.
if ( ! $url[1] ) {
return $image_html;
// Return early if the `src` value is empty.
if ( ! $src ) {
return $image;
}

$image_filename = basename( $url[1] );

/*
* First, see if the file is the full size image. If not, we loop through
* the intermediate sizes until we find a match.
* the intermediate sizes until we find a file that matches.
*/
$image_filename = wp_basename( $src );

if ( $image_filename === basename( $meta['file'] ) ) {
$size = 'full';
} else {
Expand All @@ -421,23 +388,26 @@ function _tevkori_filter_content_images_callback( $image ) {
}

// If we have an ID and size, try for 'srcset' and 'sizes' and update the markup.
if ( $id && $size && $srcset = tevkori_get_srcset_string( $id, $size ) ) {
if ( $id && $size && $srcset = tevkori_get_srcset( $id, $size ) ) {

// Pass height and width to `tevkori_get_sizes_string()`.
/**
* Pass the 'height' and 'width' to 'wp_get_attachment_image_sizes()' to avoid
* recalculating the image size.
*/
$args = array(
'width' => $width,
'height' => $height,
'width' => $width,
);
$sizes = tevkori_get_sizes( $id, $size, $args );

$sizes = tevkori_get_sizes_string( $id, $size, $args );

// Strip trailing slashes and whitespaces from the `$atts` string.
$atts = trim( rtrim( $atts, '/' ) );
// Format the srcset and sizes string and escape attributes.
$srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes) );

$image_html = "<img " . $atts . " " . $srcset . " " . $sizes . " />";
// Add srcset and sizes attributes to the image markup.
$image = preg_replace( '/<img ([^>]+)[\s?][\/?]>/', '<img $1' . $srcset_and_sizes . ' />', $image );
};

return $image_html;
return $image;
}

/**
Expand Down

0 comments on commit 3ded293

Please sign in to comment.