diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 30df3226ce..fc69bdcdc8 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -813,13 +813,14 @@ function wp_get_attachment_image($attachment_id, $size = 'thumbnail', $icon = fa // Generate srcset and sizes if not already present. if ( empty( $attr['srcset'] ) ) { - $srcset = wp_get_attachment_image_srcset( $attachment_id, $size ); - $sizes = wp_get_attachment_image_sizes( $attachment_id, $size, $width ); + $image_meta = wp_get_attachment_metadata( $attachment_id ); + $size_array = array( absint( $width ), absint( $height ) ); + $sources = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id ); - if ( $srcset && $sizes ) { - $attr['srcset'] = $srcset; + if ( count( $sources ) > 1 ) { + $attr['srcset'] = wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id ); - if ( empty( $attr['sizes'] ) ) { + if ( empty( $attr['sizes'] ) && ( $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id ) ) ) { $attr['sizes'] = $sizes; } } @@ -864,15 +865,118 @@ function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon } /** - * Retrieves an array of URLs and pixel widths representing sizes of an image. - * - * The purpose is to populate a source set when creating responsive image markup. + * Private, do not use + */ +function _wp_upload_dir_baseurl() { + static $baseurl = null; + + if ( ! $baseurl ) { + $uploads_dir = wp_upload_dir(); + $baseurl = $uploads_dir['baseurl']; + } + + return $baseurl; +} + +/** + * Private, do not use + */ +function _wp_get_image_size_from_meta( $size, $image_meta ) { + if ( $size === 'full' ) { + return array( + absint( $image_meta['width'] ), + absint( $image_meta['height'] ), + ); + } elseif ( ! empty( $image_meta['sizes'][$size] ) ) { + return array( + absint( $image_meta['sizes'][$size]['width'] ), + absint( $image_meta['sizes'][$size]['height'] ), + ); + } + + return false; +} + +/** + * Retrieves the value for an image attachment's 'srcset' attribute. * * @since 4.4.0 * - * @param int $attachment_id Image attachment ID. + + * @param int $attachment_id Optional. Image attachment ID. * @param array|string $size Image size. Accepts any valid image size, or an array of width and height * values in pixels (in that order). Default 'medium'. + * @param array $image_meta Optional. The image meta data. + * @return string|bool A 'srcset' value string or false. + */ +function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) { + if ( ! $image = wp_get_attachment_image_src( $attachment_id, $size ) ) { + return false; + } + + if ( ! is_array( $image_meta ) ) { + $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); + } + + $image_url = $image[0]; + $size_array = array( + absint( $image[1] ), + absint( $image[2] ) + ); + + // Calculate the sources for the srcset. + $sources = wp_calculate_image_srcset( $image_url, $size_array, $image_meta, $attachment_id ); + + // Only return a srcset value if there is more than one source. + if ( count( $sources ) < 2 ) { + return false; + } + + return wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id ); +} + + +/** + * A helper function to concatenate and filter the srcset attribute value. + * + * @since 4.4.0 + * + * @param array $sources The array containing image sizes data as returned by wp_calculate_image_srcset(). + * @param array $size_array Array of width and height values in pixels (in that order). + * @param array $image_meta The image meta data. + * @param int $attachment_id The image attachment post id to pass to the filter. + * @return string The srcset attribute value. + */ +function wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id ) { + $srcset = ''; + + foreach ( $sources as $source ) { + $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', '; + } + + /** + * Filter the output of wp_get_attachment_image_srcset(). + * + * @since 4.4.0 + * + * @param string $srcset A source set formatted for a `srcset` attribute. + * @param int $attachment_id Image attachment ID. + * @param array|string $size Image size. Accepts any valid image size, or an array of width and height + * values in pixels (in that order). Default 'medium'. + * @param array $image_meta The image meta data. + */ + return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size_array, $image_meta ); +} + +/** + * A helper function to caclulate the image sources to include in a srcset attribute. + * + * @since 4.4.0 + * + * @param string $image_name The file name, path, URL or partial path or URL of the image being matched. + * @param array $size_array Array of width and height values in pixels (in that order). + * @param array $image_meta The image meta data. + * @param int $attachment_id Optional. The image attachment post id to pass to the filter. * @return array|bool $sources { * Array image candidate values containing a URL, descriptor type, and * descriptor value. False if none exist. @@ -886,103 +990,91 @@ function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon * } * */ -function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' ) { - // Get the intermediate size. - $image = image_get_intermediate_size( $attachment_id, $size ); - - // Get the post meta. - $img_meta = wp_get_attachment_metadata( $attachment_id ); - if ( ! is_array( $img_meta ) ) { +function wp_calculate_image_srcset( $image_name, $size_array, $image_meta, $attachment_id = 0 ) { + if ( empty( $image_meta['sizes'] ) ) { return false; } - // Extract the height and width from the intermediate or the full size. - $img_width = ( $image ) ? $image['width'] : $img_meta['width']; - $img_height = ( $image ) ? $image['height'] : $img_meta['height']; + $image_sizes = $image_meta['sizes']; - // Bail early if the width isn't greater than zero. - if ( ! $img_width > 0 ) { + // Get the height and width for the image. + $image_width = (int) $size_array[0]; + $image_height = (int) $size_array[1]; + + // Bail early if error/no width. + if ( $image_width < 1 ) { return false; } - // Use the URL from the intermediate size or build the url from the metadata. - if ( ! empty( $image['url'] ) ) { - $img_url = $image['url']; - } else { - $uploads_dir = wp_upload_dir(); - $img_file = ( $image ) ? path_join( dirname( $img_meta['file'] ) , $image['file'] ) : $img_meta['file']; - $img_url = $uploads_dir['baseurl'] . '/' . $img_file; - } - - $img_sizes = $img_meta['sizes']; - // Add full size to the img_sizes array. - $img_sizes['full'] = array( - 'width' => $img_meta['width'], - 'height' => $img_meta['height'], - 'file' => wp_basename( $img_meta['file'] ) + $image_sizes['full'] = array( + 'width' => $image_meta['width'], + 'height' => $image_meta['height'], + 'file' => wp_basename( $image_meta['file'] ), ); + $image_baseurl = _wp_upload_dir_baseurl(); + $dirname = dirname( $image_meta['file'] ); + + if ( $dirname !== '.' ) { + $image_baseurl = path_join( $image_baseurl, $dirname ); + } + // Calculate the image aspect ratio. - $img_ratio = $img_height / $img_width; + $image_ratio = $image_height / $image_width; /* * Images that have been edited in WordPress after being uploaded will * contain a unique hash. Look for that hash and use it later to filter * out images that are leftovers from previous versions. */ - $img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash ); + $image_edited = preg_match( '/-e[0-9]{13}/', $image_name, $image_edit_hash ); /** - * Filter the maximum width included in a srcset attribute. + * Filter the maximum width included in a 'srcset' attribute. * * @since 4.4.0 * - * @param array|string $size Size of image, either array or string. + * @param int $max_width The maximum width to include in the 'srcset'. Default '1600'. + * @param array|string $size_array Array of width and height values in pixels (in that order). */ - $max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size ); + $max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size_array ); + + // Array to hold URL candidates. + $sources = array(); /* - * Set up arrays to hold url candidates and matched image sources so - * we can avoid duplicates without looping through the full sources array + * Loop through available images. Only use images that are resized + * versions of the same edit. */ - $candidates = $sources = array(); + foreach ( $image_sizes as $image ) { - /* - * Loop through available images and only use images that are resized - * versions of the same rendition. - */ - foreach ( $img_sizes as $img ) { - - // Filter out images that are leftovers from previous renditions. - if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) { + // Filter out images that are from previous edits. + if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) { continue; } // Filter out images that are wider than $max_srcset_width. - if ( $max_srcset_width && $img['width'] > $max_srcset_width ) { + if ( $max_srcset_width && $image['width'] > $max_srcset_width ) { continue; } - $candidate_url = path_join( dirname( $img_url ), $img['file'] ); + $candidate_url = $image['file']; // Calculate the new image ratio. - if ( $img['width'] ) { - $img_ratio_compare = $img['height'] / $img['width']; + if ( $image['width'] ) { + $image_ratio_compare = $image['height'] / $image['width']; } else { - $img_ratio_compare = 0; + $image_ratio_compare = 0; } // If the new ratio differs by less than 0.01, use it. - if ( abs( $img_ratio - $img_ratio_compare ) < 0.01 && ! in_array( $candidate_url, $candidates ) ) { - // Add the URL to our list of candidates. - $candidates[] = $candidate_url; - - // Add the url, descriptor, and value to the sources array to be returned. - $sources[] = array( - 'url' => $candidate_url, + if ( abs( $image_ratio - $image_ratio_compare ) < 0.01 && ! array_key_exists( $candidate_url, $sources ) ) { + // Add the URL, descriptor, and value to the sources array to be returned. + $sources[ $image['width'] ] = array( + 'url' => path_join( $image_baseurl, $candidate_url ), 'descriptor' => 'w', - 'value' => $img['width'], + 'value' => $image['width'], ); } } @@ -992,81 +1084,55 @@ function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' * * @since 4.4.0 * - * @param array $sources An array of image urls and widths. + * @param array $sources An array of image URLs and widths. * @param int $attachment_id Attachment ID for image. * @param array|string $size Image size. Accepts any valid image size, or an array of width and height * values in pixels (in that order). Default 'medium'. + * @param array $image_meta The image meta data. + */ - return apply_filters( 'wp_get_attachment_image_srcset_array', $sources, $attachment_id, $size ); + return apply_filters( 'wp_get_attachment_image_srcset_array', array_values( $sources ), $attachment_id, $size_array, $image_meta ); } /** - * Retrieves the value for an image attachment's 'srcset' attribute. + * Create `sizes` attribute value for an image. * * @since 4.4.0 * - * @param int $attachment_id Image attachment ID. - * @param array|string $size Image size. Accepts any valid image size, or an array of width and height - * values in pixels (in that order). Default 'medium'. - * @return string|bool A 'srcset' value string or false. - */ -function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium' ) { - $srcset_array = wp_get_attachment_image_srcset_array( $attachment_id, $size ); - - // Only return a srcset value if there is more than one source. - if ( count( $srcset_array ) <= 1 ) { - return false; - } - - $srcset = ''; - foreach ( $srcset_array as $source ) { - $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', '; - } - - /** - * Filter the output of wp_get_attachment_image_srcset(). - * - * @since 4.4.0 - * - * @param string $srcset A source set formated for a `srcset` attribute. - * @param int $attachment_id Attachment ID for image. - * @param array|string $size Image size. Accepts any valid image size, or an array of width and height - * values in pixels (in that order). Default 'medium'. - */ - return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size ); -} - -/** - * Retrieves a source size attribute for an image from an array of values. + * @param array|string $size Image size. Accepts any valid image size name (thumbnail, medium, etc.), + * or an array of width and height values in pixels (in that order). + * @param array $image_meta Optional. The image meta data. + * @param int $attachment_id Optional. Image attachment ID. Either $image_meta or $attachment_id is needed + * when using image size name. * - * @since 4.4.0 - * - * @param int $attachment_id Image attachment ID. - * @param array|string $size Image size. Accepts any valid image size, or an array of width and height - * values in pixels (in that order). Default 'medium'. - * @param int $width Optional. Display width of the image. * @return string|bool A valid source size value for use in a 'sizes' attribute or false. */ -function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width = null ) { - // Try to get the image width from the $width parameter. - if ( is_numeric( $width ) ) { - $img_width = (int) $width; - // Next, see if a width value was passed in the $size parameter. +function wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment_id = 0 ) { + $width = 0; + + if ( is_numeric( $size ) ) { + $width = absint( $size ); } elseif ( is_array( $size ) ) { - $img_width = $size[0]; - // Finally, use the $size name to return the width of the image. - } else { - $image = image_get_intermediate_size( $attachment_id, $size ); - $img_width = $image ? $image['width'] : false; + $width = absint( $size[0] ); + } elseif ( is_string( $size ) ) { + if ( ! $image_meta && $attachment_id ) { + $image_meta = wp_get_attachment_metadata( $attachment_id ); + } + + if ( $image_meta ) { + $width = _wp_get_image_size_from_meta( $size, $image_meta ); + if ( $width ) { + $width = $width[0]; + } + } } - // Bail early if $img_width isn't set. - if ( ! $img_width ) { + if ( ! $width ) { return false; } // Setup the default sizes attribute. - $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $img_width ); + $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', (int) $width ); /** * Filter the output of wp_get_attachment_image_sizes(). @@ -1074,12 +1140,12 @@ function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width * @since 4.4.0 * * @param string $sizes A source size value for use in a 'sizes' attribute. - * @param int $attachment_id Post ID of the original image. * @param array|string $size Image size. Accepts any valid image size, or an array of width and height * values in pixels (in that order). Default 'medium'. - * @param int $width Display width of the image. + * @param array $image_meta The image meta data. + * @param int $attachment_id Post ID of the original image. */ - return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $attachment_id, $size, $width ); + return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $size, $image_meta, $attachment_id ); } /** @@ -1087,7 +1153,7 @@ function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width * * @since 4.4.0 * - * @see wp_img_add_srcset_and_sizes() + * @see wp_image_add_srcset_and_sizes() * * @param string $content The raw post content to be filtered. * @return string Converted content with 'srcset' and 'sizes' attributes added to images. @@ -1095,29 +1161,33 @@ function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width function wp_make_content_images_responsive( $content ) { $images = get_media_embedded_in_content( $content, 'img' ); - $attachment_ids = array(); + $selected_images = $attachment_ids = array(); foreach( $images as $image ) { - if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) { - $attachment_id = (int) $class_id[1]; - if ( $attachment_id ) { - $attachment_ids[] = $attachment_id; - } + if ( false === strpos( $image, ' srcset="' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) && + ( $attachment_id = absint( $class_id[1] ) ) ) { + + // If exactly the same image tag is used more than once, overwrite it. + // All identical tags will be replaced later with str_replace(). + $selected_images[ $image ] = $attachment_id; + // Overwrite the ID when the same image is included more than once. + $attachment_ids[ $attachment_id ] = true; } } - if ( 0 < count( $attachment_ids ) ) { + if ( count( $attachment_ids ) > 1 ) { /* - * Warm object caches for use with wp_get_attachment_metadata. + * Warm object cache for use with get_post_meta(). * * 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( $attachment_ids, false, true ); + update_meta_cache( 'post', array_keys( $attachment_ids ) ); } - foreach( $images as $image ) { - $content = str_replace( $image, wp_img_add_srcset_and_sizes( $image ), $content ); + foreach ( $selected_images as $image => $attachment_id ) { + $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); + $content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content ); } return $content; @@ -1131,74 +1201,76 @@ function wp_make_content_images_responsive( $content ) { * @see wp_get_attachment_image_srcset() * @see wp_get_attachment_image_sizes() * - * @param string $image An HTML 'img' element to be filtered. + * @param string $image An HTML 'img' element to be filtered. + * @param array $image_meta The image meta data. + * @param int $attachment_id Image attachment ID. * @return string Converted 'img' element with `srcset` and `sizes` attributes added. */ -function wp_img_add_srcset_and_sizes( $image ) { - // Return early if a 'srcset' attribute already exists. - if ( false !== strpos( $image, ' srcset="' ) ) { +function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) { + // Ensure the image meta exists + if ( empty( $image_meta['sizes'] ) ) { return $image; } - // 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; + $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : ''; + list( $src ) = explode( '?', $src ); - if ( $id && ! $size ) { - $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : false; - - if ( $width && $height ) { - $size = array( $width, $height ); - } + // Return early if we coudn't get the image source. + if ( ! $src ) { + return $image; } - /* - * If attempts to parse the size value failed, attempt to use the image - * metadata to match the 'src' against the available sizes for an attachment. - */ - if ( $id && ! $size ) { - $meta = wp_get_attachment_metadata( $id ); + // Bail early when an image has been inserted and later edited. + if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) && + strpos( wp_basename( $src ), $img_edit_hash[0] ) === false ) { - // Parse the image src value from the img element. - $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false; + return $image; + } - // Return early if the metadata does not exist or the src value is empty. - if ( ! $meta || ! $src ) { - return $image; - } + $width = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0; + $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0; + if ( ! $width || ! $height ) { /* - * First, see if the file is the full size image. If not, loop through - * the intermediate sizes until we find a file that matches. + * If attempts to parse the size value failed, attempt to use the image + * metadata to match the image file name from 'src' against the available sizes for an attachment. */ $image_filename = wp_basename( $src ); - if ( $image_filename === basename( $meta['file'] ) ) { - $size = 'full'; + if ( $image_filename === wp_basename( $image_meta['file'] ) ) { + $width = (int) $image_meta['width']; + $height = (int) $image_meta['height']; } else { - foreach( $meta['sizes'] as $image_size => $image_size_data ) { + foreach( $image_meta['sizes'] as $image_size_data ) { if ( $image_filename === $image_size_data['file'] ) { - $size = $image_size; + $width = (int) $image_size_data['width']; + $height = (int) $image_size_data['height']; break; } } } - } - // If ID and size exist, try for 'srcset' and 'sizes' and update the markup. - if ( $id && $size ) { - $srcset = wp_get_attachment_image_srcset( $id, $size ); - $sizes = wp_get_attachment_image_sizes( $id, $size, $width ); + if ( ! $width || ! $height ) { + return $image; + } - if ( $srcset && $sizes ) { - // Format the srcset and sizes string and escape attributes. - $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes) ); + $size_array = array( $width, $height ); + $sources = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id ); - // Add srcset and sizes attributes to the image markup. - $image = preg_replace( '/]+)[\s?][\/?]>/', '', $image ); - } + $srcset = $sizes = ''; + // Only calculate srcset and sizes values if there is more than one source. + if ( count( $sources ) > 1 ) { + $srcset = wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id ); + $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id ); + } + + if ( $srcset && $sizes ) { + // Format the srcset and sizes string and escape attributes. + $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes ) ); + + // Add srcset and sizes attributes to the image markup. + $image = preg_replace( '/]+?)[\/ ]*>/', '', $image ); } return $image; diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index b682b09400..3a71bd2245 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -715,28 +715,46 @@ EOF; $this->assertEquals( $image[0], wp_get_attachment_image_url( $attachment_id ) ); } + /** + * Helper function to get image size array from size "name" + */ + function _get_image_size_array_from_name( $size_name ) { + switch ( $size_name ) { + case 'thumbnail': + return array( 150, 150 ); + case 'medium': + return array( 300, 225 ); + case 'large': + return array( 1024, 768 ); + case 'full': + return array( 1600, 1200 ); // actual size of ../data/images/test-image-large.png + default: + return array( 800, 600 ); // soft-resized image + } + } + /** * @ticket 33641 */ - function test_wp_get_attachment_image_srcset_array() { + function test_wp_calculate_image_srcset() { $year_month = date('Y/m'); - $image = wp_get_attachment_metadata( self::$large_id ); + $image_meta = wp_get_attachment_metadata( self::$large_id ); $expected = array( array( - 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image['sizes']['medium']['file'], + 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image_meta['sizes']['medium']['file'], 'descriptor' => 'w', - 'value' => $image['sizes']['medium']['width'], + 'value' => $image_meta['sizes']['medium']['width'], ), array( - 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image['sizes']['large']['file'], + 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image_meta['sizes']['large']['file'], 'descriptor' => 'w', - 'value' => $image['sizes']['large']['width'], + 'value' => $image_meta['sizes']['large']['width'], ), array( - 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'], + 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'], 'descriptor' => 'w', - 'value' => $image['width'], + 'value' => $image_meta['width'], ), ); @@ -744,14 +762,16 @@ EOF; $sizes = array( 'medium', 'large', 'full', 'yoav' ); foreach ( $sizes as $size ) { - $this->assertSame( $expected, wp_get_attachment_image_srcset_array( self::$large_id, $size ) ); + $image_url = wp_get_attachment_image_url( self::$large_id, $size ); + $size_array = $this->_get_image_size_array_from_name( $size ); + $this->assertSame( $expected, wp_calculate_image_srcset( $image_url, $size_array, $image_meta ) ); } } /** * @ticket 33641 */ - function test_wp_get_attachment_image_srcset_array_no_date_uploads() { + function test_wp_calculate_image_srcset_no_date_uploads() { // Save the current setting for uploads folders $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' ); @@ -762,23 +782,23 @@ EOF; $filename = DIR_TESTDATA . '/images/test-image-large.png'; $id = self::factory()->attachment->create_upload_object( $filename ); - $image = wp_get_attachment_metadata( $id ); + $image_meta = wp_get_attachment_metadata( $id ); $expected = array( array( - 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['sizes']['medium']['file'], + 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['sizes']['medium']['file'], 'descriptor' => 'w', - 'value' => $image['sizes']['medium']['width'], + 'value' => $image_meta['sizes']['medium']['width'], ), array( - 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['sizes']['large']['file'], + 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['sizes']['large']['file'], 'descriptor' => 'w', - 'value' => $image['sizes']['large']['width'], + 'value' => $image_meta['sizes']['large']['width'], ), array( - 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'], + 'url' => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'], 'descriptor' => 'w', - 'value' => $image['width'], + 'value' => $image_meta['width'], ), ); @@ -786,7 +806,9 @@ EOF; $sizes = array( 'medium', 'large', 'full', 'yoav' ); foreach ( $sizes as $size ) { - $this->assertSame( $expected, wp_get_attachment_image_srcset_array( $id, $size ) ); + $size_array = $this->_get_image_size_array_from_name( $size ); + $image_url = wp_get_attachment_image_url( self::$large_id, $size ); + $this->assertSame( $expected, wp_calculate_image_srcset( $image_url, $size_array, $image_meta ) ); } // Leave the uploads option the way you found it. @@ -796,24 +818,24 @@ EOF; /** * @ticket 33641 */ - function test_wp_get_attachment_image_srcset_array_with_edits() { + function test_wp_calculate_image_srcset_with_edits() { // For this test we're going to mock metadata changes from an edit. // Start by getting the attachment metadata. - $meta = wp_get_attachment_metadata( self::$large_id ); + $image_meta = wp_get_attachment_metadata( self::$large_id ); + $image_url = wp_get_attachment_image_url( self::$large_id ); + $size_array = $this->_get_image_size_array_from_name( 'medium' ); // Copy hash generation method used in wp_save_image(). $hash = 'e' . time() . rand(100, 999); // Replace file paths for full and medium sizes with hashed versions. - $filename_base = basename( $meta['file'], '.png' ); - $meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['file'] ); - $meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['sizes']['medium']['file'] ); - - // Save edited metadata. - wp_update_attachment_metadata( self::$large_id, $meta ); + $filename_base = basename( $image_meta['file'], '.png' ); + $image_meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['file'] ); + $image_meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['sizes']['medium']['file'] ); + $image_meta['sizes']['large']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['sizes']['large']['file'] ); // Calculate a srcset array. - $sizes = wp_get_attachment_image_srcset_array( self::$large_id, 'medium' ); + $sizes = wp_calculate_image_srcset( $image_url, $size_array, $image_meta ); // Test to confirm all sources in the array include the same edit hash. foreach ( $sizes as $size ) { @@ -824,8 +846,8 @@ EOF; /** * @ticket 33641 */ - function test_wp_get_attachment_image_srcset_array_false() { - $sizes = wp_get_attachment_image_srcset_array( 99999, 'foo' ); + function test_wp_calculate_image_srcset_false() { + $sizes = wp_calculate_image_srcset( 'file.png', array( 400, 300 ), array() ); // For canola.jpg we should return $this->assertFalse( $sizes ); @@ -834,49 +856,35 @@ EOF; /** * @ticket 33641 */ - function test_wp_get_attachment_image_srcset_array_no_width() { - // Filter image_downsize() output. - add_filter( 'wp_generate_attachment_metadata', array( $this, '_test_wp_get_attachment_image_srcset_array_no_width_filter' ) ); - - $old_meta = get_post_meta( self::$large_id, '_wp_attachment_metadata', true ); + function test_wp_calculate_image_srcset_no_width() { $file = get_attached_file( self::$large_id ); + $image_url = wp_get_attachment_image_url( self::$large_id, 'medium' ); + $image_meta = wp_generate_attachment_metadata( self::$large_id, $file ); - $data = wp_generate_attachment_metadata( self::$large_id, $file ); - wp_update_attachment_metadata( self::$large_id, $data ); + $size_array = array(0, 0); - $srcset = wp_get_attachment_image_srcset_array( self::$large_id, 'medium' ); - - update_post_meta( self::$large_id, '_wp_attachment_metadata', $old_meta ); + $srcset = wp_calculate_image_srcset( $image_url, $size_array, $image_meta ); // The srcset should be false. $this->assertFalse( $srcset ); } - /** - * Helper function to filter image_downsize and return zero values for width and height. - */ - public function _test_wp_get_attachment_image_srcset_array_no_width_filter( $meta ) { - remove_filter( 'wp_generate_attachment_metadata', array( $this, __FUNCTION__ ) ); - - $meta['sizes']['medium']['width'] = 0; - $meta['sizes']['medium']['height'] = 0; - return $meta; - } - /** * @ticket 33641 */ function test_wp_get_attachment_image_srcset() { - $sizes = wp_get_attachment_image_srcset( self::$large_id, 'full-size' ); + $image_meta = wp_get_attachment_metadata( self::$large_id ); + $size_array = array( 1600, 1200 ); // full size + + $sizes = wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta ); - $image = wp_get_attachment_metadata( self::$large_id ); $year_month = date('Y/m'); $expected = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w, '; + . $image_meta['sizes']['medium']['file'] . ' ' . $image_meta['sizes']['medium']['width'] . 'w, '; $expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month = date('Y/m') . '/' - . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w, '; - $expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; + . $image_meta['sizes']['large']['file'] . ' ' . $image_meta['sizes']['large']['width'] . 'w, '; + $expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w'; $this->assertSame( $expected, $sizes ); } @@ -885,11 +893,13 @@ EOF; * @ticket 33641 */ function test_wp_get_attachment_image_srcset_single_srcset() { + $image_meta = wp_get_attachment_metadata( self::$large_id ); + $size_array = array( 150, 150 ); /* * In our tests, thumbnails will only return a single srcset candidate, * so we shouldn't return a srcset value in order to avoid unneeded markup. */ - $sizes = wp_get_attachment_image_srcset( self::$large_id, 'thumbnail' ); + $sizes = wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta ); $this->assertFalse( $sizes ); } @@ -899,76 +909,57 @@ EOF; */ function test_wp_get_attachment_image_sizes() { // Test sizes against the default WP sizes. - $intermediates = array('thumbnail', 'medium', 'large'); + $intermediates = array( 'thumbnail', 'medium', 'large' ); + $image_meta = wp_get_attachment_metadata( self::$large_id ); - foreach( $intermediates as $int ) { - $width = get_option( $int . '_size_w' ); + foreach( $intermediates as $int_size ) { + $size_array = $this->_get_image_size_array_from_name( $int_size ); + list( $width, $height ) = $size_array; $expected = '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px'; - $sizes = wp_get_attachment_image_sizes( self::$large_id, $int ); + $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta ); - $this->assertSame($expected, $sizes); + $this->assertSame( $expected, $sizes ); } } - /** - * @ticket 33641 - */ - function test_wp_get_attachment_image_sizes_with_width() { - $width = 350; - - $expected = '(max-width: 350px) 100vw, 350px'; - $sizes = wp_get_attachment_image_sizes( self::$large_id, 'medium', $width ); - - $this->assertSame( $expected, $sizes ); - } - /** * @ticket 33641 */ function test_wp_make_content_images_responsive() { - $srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, 'medium' ) ); - $sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( self::$large_id, 'medium' ) ); + $image_meta = wp_get_attachment_metadata( self::$large_id ); + $size_array = $this->_get_image_size_array_from_name( 'medium' ); + + $srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta ) ); + $sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( $size_array, $image_meta, self::$large_id ) ); // Function used to build HTML for the editor. $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); - $img_no_size = str_replace( 'size-', '', $img ); - $img_no_size_id = str_replace( 'wp-image-', 'id-', $img_no_size ); + $img_no_size_in_class = str_replace( 'size-', '', $img ); + $img_no_width_height = str_replace( ' width="' . $size_array[0] . '"', '', $img ); + $img_no_width_height = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height ); + $img_no_size_id = str_replace( 'wp-image-', 'id-', $img ); // Manually add srcset and sizes to the markup from get_image_tag(); - $respimg = preg_replace('|]+) />|', '', $img); - $respimg_no_size = preg_replace('|]+) />|', '', $img_no_size); - - $content = '

Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.

-

First things first:

+ $respimg = preg_replace( '|]+) />|', '', $img ); + $respimg_no_size_in_class = preg_replace( '|]+) />|', '', $img_no_size_in_class ); + $respimg_no_width_height = preg_replace( '|]+) />|', '', $img_no_width_height ); + $content = ' +

Image, standard. Should have srcset and sizes.

%1$s - - +

Image, no size class. Should have srcset and sizes.

%2$s -

As a subscriber, you will receive an email every time an update is available (and only then). This will make it easier to keep your site up to date, and secure from evildoers.
- When a new version is released, log in to the Dashboard and follow the instructions.
- Upgrading is a couple of clicks!

- +

Image, no width and height attributes. Should have srcset and sizes (from matching the file name).

%3$s -

Then you can start enjoying the WordPress experience:

- '; +

Image, no attachment ID class. Should NOT have srcset and sizes.

+ %4$s'; - $content_unfiltered = sprintf( $content, $img, $img_no_size, $img_no_size_id ); - $content_filtered = sprintf( $content, $respimg, $respimg_no_size, $img_no_size_id ); + $content_unfiltered = sprintf( $content, $img, $img_no_size_in_class, $img_no_width_height, $img_no_size_id ); + $content_filtered = sprintf( $content, $respimg, $respimg_no_size_in_class, $respimg_no_width_height, $img_no_size_id ); $this->assertSame( $content_filtered, wp_make_content_images_responsive( $content_unfiltered ) ); }