Make WordPress Core

Ticket #34430: 34430.5.diff

File 34430.5.diff, 34.8 KB (added by joemcgill, 9 years ago)
  • src/wp-includes/media.php

    diff --git src/wp-includes/media.php src/wp-includes/media.php
    index 30df322..ee4a860 100644
    function wp_get_attachment_image($attachment_id, $size = 'thumbnail', $icon = fa 
    813813
    814814                // Generate srcset and sizes if not already present.
    815815                if ( empty( $attr['srcset'] ) ) {
    816                         $srcset = wp_get_attachment_image_srcset( $attachment_id, $size );
    817                         $sizes  = wp_get_attachment_image_sizes( $attachment_id, $size, $width );
     816                        $image_meta = wp_get_attachment_metadata( $attachment_id );
     817                        $size_array = array( absint( $width ), absint( $height ) );
     818                        $srcset = _wp_calculate_image_srcset( $src, $size_array, $image_meta );
    818819
    819                         if ( $srcset && $sizes ) {
     820                        if ( $srcset ) {
    820821                                $attr['srcset'] = $srcset;
    821822
    822                                 if ( empty( $attr['sizes'] ) ) {
     823                                if ( empty( $attr['sizes'] ) && ( $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id ) ) ) {
    823824                                        $attr['sizes'] = $sizes;
    824825                                }
    825826                        }
    function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon 
    864865}
    865866
    866867/**
    867  * Retrieves an array of URLs and pixel widths representing sizes of an image.
    868  *
    869  * The purpose is to populate a source set when creating responsive image markup.
     868 * Private, do not use
     869 */
     870function _wp_upload_dir_baseurl() {
     871        static $baseurl = null;
     872
     873        if ( ! $baseurl ) {
     874                $uploads_dir = wp_upload_dir();
     875                $baseurl = $uploads_dir['baseurl'];
     876        }
     877
     878        return $baseurl;
     879}
     880
     881/**
     882 * Private, do not use
     883 */
     884function _wp_get_image_size_from_meta( $size, $image_meta ) {
     885        if ( $size === 'full' ) {
     886                return array(
     887                        absint( $image_meta['width'] ),
     888                        absint( $image_meta['height'] ),
     889                );
     890        } elseif ( ! empty( $image_meta['sizes'][$size] ) ) {
     891                return array(
     892                        absint( $image_meta['sizes'][$size]['width'] ),
     893                        absint( $image_meta['sizes'][$size]['height'] ),
     894                );
     895        }
     896
     897        return false;
     898}
     899
     900/**
     901 * Retrieves the value for an image attachment's 'srcset' attribute.
    870902 *
    871903 * @since 4.4.0
    872904 *
    873  * @param int          $attachment_id Image attachment ID.
     905
     906 * @param int          $attachment_id Optional. Image attachment ID.
    874907 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    875908 *                                    values in pixels (in that order). Default 'medium'.
     909 * @param array        $image_meta    Optional. The image meta data.
     910 * @return string|bool A 'srcset' value string or false.
     911 */
     912function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) {
     913        if ( ! $image = wp_get_attachment_image_src( $attachment_id, $size ) ) {
     914                return false;
     915        }
     916
     917        if ( ! is_array( $image_meta ) ) {
     918                $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
     919        }
     920
     921        $image_url = $image[0];
     922        $size_array = array(
     923                absint( $image[1] ),
     924                absint( $image[2] )
     925        );
     926
     927        // Calculate the sources for the srcset.
     928        $sources = _wp_calculate_image_srcset( $image_url, $size_array, $image_meta, $attachment_id );
     929
     930        // Only return a srcset value if there is more than one source.
     931        if ( count( $sources ) < 2 ) {
     932                return false;
     933        }
     934
     935        $srcset = '';
     936        foreach ( $sources as $source ) {
     937                $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
     938        }
     939
     940        /**
     941         * Filter the output of wp_get_attachment_image_srcset().
     942         *
     943         * @since 4.4.0
     944         *
     945         * @param string       $srcset        A source set formatted for a `srcset` attribute.
     946         * @param int          $attachment_id Image attachment ID.
     947         * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
     948         *                                    values in pixels (in that order). Default 'medium'.
     949         * @param array        $image_meta    The image meta data.
     950         */
     951        return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size, $image_meta );
     952}
     953
     954/**
     955 * A helper function to caclulate the image sources to include in a srcset attribute.
     956 *
     957 * @since 4.4.0
     958 *
     959 * @param string $image_url     The URL of the image being matched.
     960 * @param array  $size_array    Array of width and height values in pixels (in that order).
     961 * @param array  $image_meta    The image meta data.
     962 * @param int    $attachment_id Optional. The image id to pass to the filter.
    876963 * @return array|bool $sources {
    877964 *     Array image candidate values containing a URL, descriptor type, and
    878965 *     descriptor value. False if none exist.
    function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon 
    886973 * }
    887974 *
    888975 */
    889 function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' ) {
    890         // Get the intermediate size.
    891         $image = image_get_intermediate_size( $attachment_id, $size );
    892 
    893         // Get the post meta.
    894         $img_meta = wp_get_attachment_metadata( $attachment_id );
    895         if ( ! is_array( $img_meta ) ) {
     976function _wp_calculate_image_srcset( $image_url, $size_array, $image_meta, $attachment_id = 0 ) {
     977        if ( empty( $image_meta['sizes'] ) ) {
    896978                return false;
    897979        }
    898980
    899         // Extract the height and width from the intermediate or the full size.
    900         $img_width  = ( $image ) ? $image['width']  : $img_meta['width'];
    901         $img_height = ( $image ) ? $image['height'] : $img_meta['height'];
     981        $image_sizes = $image_meta['sizes'];
    902982
    903         // Bail early if the width isn't greater than zero.
    904         if ( ! $img_width > 0 ) {
    905                 return false;
    906         }
     983        // Get the height and width for the image.
     984        $image_width = (int) $size_array[0];
     985        $image_height = (int) $size_array[1];
    907986
    908         // Use the URL from the intermediate size or build the url from the metadata.
    909         if ( ! empty( $image['url'] ) ) {
    910                 $img_url = $image['url'];
    911         } else {
    912                 $uploads_dir = wp_upload_dir();
    913                 $img_file = ( $image ) ? path_join( dirname( $img_meta['file'] ) , $image['file'] ) : $img_meta['file'];
    914                 $img_url = $uploads_dir['baseurl'] . '/' . $img_file;
     987        // Bail early if error/no width.
     988        if ( $image_width < 1 ) {
     989                return false;
    915990        }
    916991
    917         $img_sizes = $img_meta['sizes'];
    918 
    919992        // Add full size to the img_sizes array.
    920         $img_sizes['full'] = array(
    921                 'width'  => $img_meta['width'],
    922                 'height' => $img_meta['height'],
    923                 'file'   => wp_basename( $img_meta['file'] )
     993        $image_sizes['full'] = array(
     994                'width'  => $image_meta['width'],
     995                'height' => $image_meta['height'],
     996                'file'   => wp_basename( $image_meta['file'] ),
    924997        );
    925998
     999        $image_baseurl = _wp_upload_dir_baseurl();
     1000        $dirname = dirname( $image_meta['file'] );
     1001
     1002        if ( $dirname !== '.' ) {
     1003                $image_baseurl = path_join( $image_baseurl, $dirname );
     1004        }
     1005
    9261006        // Calculate the image aspect ratio.
    927         $img_ratio = $img_height / $img_width;
     1007        $image_ratio = $image_height / $image_width;
    9281008
    9291009        /*
    9301010         * Images that have been edited in WordPress after being uploaded will
    9311011         * contain a unique hash. Look for that hash and use it later to filter
    9321012         * out images that are leftovers from previous versions.
    9331013         */
    934         $img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash );
     1014        $image_edited = preg_match( '/-e[0-9]{13}/', $image_url, $image_edit_hash );
    9351015
    9361016        /**
    937          * Filter the maximum width included in a srcset attribute.
     1017         * Filter the maximum width included in a 'srcset' attribute.
    9381018         *
    9391019         * @since 4.4.0
    9401020         *
    941          * @param array|string $size Size of image, either array or string.
     1021         * @param int          $max_width  The maximum width to include in the 'srcset'. Default '1600'.
     1022         * @param array|string $size_array Array of width and height values in pixels (in that order).
    9421023         */
    943         $max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size );
     1024        $max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size_array );
    9441025
    945         /*
    946          * Set up arrays to hold url candidates and matched image sources so
    947          * we can avoid duplicates without looping through the full sources array
    948          */
    949         $candidates = $sources = array();
     1026        // Array to hold URL candidates.
     1027        $sources = array();
    9501028
    9511029        /*
    952          * Loop through available images and only use images that are resized
    953          * versions of the same rendition.
     1030         * Loop through available images. Only use images that are resized
     1031         * versions of the same edit.
    9541032         */
    955         foreach ( $img_sizes as $img ) {
     1033        foreach ( $image_sizes as $image ) {
    9561034
    957                 // Filter out images that are leftovers from previous renditions.
    958                 if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) {
     1035                // Filter out images that are from previous edits.
     1036                if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
    9591037                        continue;
    9601038                }
    9611039
    9621040                // Filter out images that are wider than $max_srcset_width.
    963                 if ( $max_srcset_width && $img['width'] > $max_srcset_width ) {
     1041                if ( $max_srcset_width && $image['width'] > $max_srcset_width ) {
    9641042                        continue;
    9651043                }
    9661044
    967                 $candidate_url = path_join( dirname( $img_url ), $img['file'] );
     1045                $candidate_url = $image['file'];
    9681046
    9691047                // Calculate the new image ratio.
    970                 if ( $img['width'] ) {
    971                         $img_ratio_compare = $img['height'] / $img['width'];
     1048                if ( $image['width'] ) {
     1049                        $image_ratio_compare = $image['height'] / $image['width'];
    9721050                } else {
    973                         $img_ratio_compare = 0;
     1051                        $image_ratio_compare = 0;
    9741052                }
    9751053
    9761054                // If the new ratio differs by less than 0.01, use it.
    977                 if ( abs( $img_ratio - $img_ratio_compare ) < 0.01 && ! in_array( $candidate_url, $candidates ) ) {
    978                         // Add the URL to our list of candidates.
    979                         $candidates[] = $candidate_url;
    980 
    981                         // Add the url, descriptor, and value to the sources array to be returned.
    982                         $sources[] = array(
    983                                 'url'        => $candidate_url,
     1055                if ( abs( $image_ratio - $image_ratio_compare ) < 0.01 && ! array_key_exists( $candidate_url, $sources ) ) {
     1056                        // Add the URL, descriptor, and value to the sources array to be returned.
     1057                        $sources[ $image['width'] ] = array(
     1058                                'url'        => path_join( $image_baseurl, $candidate_url ),
    9841059                                'descriptor' => 'w',
    985                                 'value'      => $img['width'],
     1060                                'value'      => $image['width'],
    9861061                        );
    9871062                }
    9881063        }
    function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' 
    9921067         *
    9931068         * @since 4.4.0
    9941069         *
    995          * @param array        $sources       An array of image urls and widths.
     1070         * @param array        $sources       An array of image URLs and widths.
    9961071         * @param int          $attachment_id Attachment ID for image.
    9971072         * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    9981073         *                                    values in pixels (in that order). Default 'medium'.
    999          */
    1000         return apply_filters( 'wp_get_attachment_image_srcset_array', $sources, $attachment_id, $size );
    1001 }
    1002 
    1003 /**
    1004  * Retrieves the value for an image attachment's 'srcset' attribute.
    1005  *
    1006  * @since 4.4.0
    1007  *
    1008  * @param int          $attachment_id Image attachment ID.
    1009  * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    1010  *                                    values in pixels (in that order). Default 'medium'.
    1011  * @return string|bool A 'srcset' value string or false.
    1012  */
    1013 function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium' ) {
    1014         $srcset_array = wp_get_attachment_image_srcset_array( $attachment_id, $size );
    1015 
    1016         // Only return a srcset value if there is more than one source.
    1017         if ( count( $srcset_array ) <= 1 ) {
    1018                 return false;
    1019         }
    1020 
    1021         $srcset = '';
    1022         foreach ( $srcset_array as $source ) {
    1023                 $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
    1024         }
     1074         * @param array        $image_meta    The image meta data.
    10251075
    1026         /**
    1027          * Filter the output of wp_get_attachment_image_srcset().
    1028          *
    1029          * @since 4.4.0
    1030          *
    1031          * @param string       $srcset        A source set formated for a `srcset` attribute.
    1032          * @param int          $attachment_id Attachment ID for image.
    1033          * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    1034          *                                    values in pixels (in that order). Default 'medium'.
    10351076         */
    1036         return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size );
     1077        return apply_filters( 'wp_get_attachment_image_srcset_array', array_values( $sources ), $attachment_id, $size_array, $image_meta );
    10371078}
    10381079
    10391080/**
    1040  * Retrieves a source size attribute for an image from an array of values.
     1081 * Create `sizes` attribute value for an image.
    10411082 *
    10421083 * @since 4.4.0
    10431084 *
    1044  * @param int          $attachment_id Image attachment ID.
    10451085 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    10461086 *                                    values in pixels (in that order). Default 'medium'.
    1047  * @param int          $width         Optional. Display width of the image.
     1087 * @param array        $image_meta    Optional. The image meta data.
     1088* @param int          $attachment_id Optional. Image attachment ID.
     1089 *
    10481090 * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
    10491091 */
    1050 function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width = null ) {
    1051         // Try to get the image width from the $width parameter.
    1052         if ( is_numeric( $width ) ) {
    1053                 $img_width = (int) $width;
    1054         // Next, see if a width value was passed in the $size parameter.
     1092function wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment_id = 0 ) {
     1093        $width = 0;
     1094
     1095        if ( is_numeric( $size ) ) {
     1096                $width = absint( $size );
    10551097        } elseif ( is_array( $size ) ) {
    1056                 $img_width = $size[0];
    1057         // Finally, use the $size name to return the width of the image.
    1058         } else {
    1059                 $image = image_get_intermediate_size( $attachment_id, $size );
    1060                 $img_width = $image ? $image['width'] : false;
     1098                $width = absint( $size[0] );
     1099        }
     1100
     1101        if ( ! $width && ( $image_meta || $attachment_id ) ) {
     1102                $image_meta = ( $image_meta ) ? $image_meta : wp_get_attachment_metadata( $attachment_id );
     1103                $width = _wp_get_image_size_from_meta( $size, $image_meta )[0];
    10611104        }
    10621105
    1063         // Bail early if $img_width isn't set.
    1064         if ( ! $img_width ) {
     1106        if ( ! $width ) {
    10651107                return false;
    10661108        }
    10671109
    10681110        // Setup the default sizes attribute.
    1069         $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $img_width );
     1111        $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', (int) $width );
    10701112
    10711113        /**
    10721114         * Filter the output of wp_get_attachment_image_sizes().
    function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width 
    10741116         * @since 4.4.0
    10751117         *
    10761118         * @param string       $sizes         A source size value for use in a 'sizes' attribute.
    1077          * @param int          $attachment_id Post ID of the original image.
    10781119         * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    10791120         *                                    values in pixels (in that order). Default 'medium'.
    1080          * @param int          $width         Display width of the image.
     1121         * @param array        $image_meta    The image meta data.
     1122         * @param int          $attachment_id Post ID of the original image.
    10811123         */
    1082         return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $attachment_id, $size, $width );
     1124        return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $size, $image_meta, $attachment_id );
    10831125}
    10841126
    10851127/**
    function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width 
    10871129 *
    10881130 * @since 4.4.0
    10891131 *
    1090  * @see wp_img_add_srcset_and_sizes()
     1132 * @see wp_image_add_srcset_and_sizes()
    10911133 *
    10921134 * @param string $content The raw post content to be filtered.
    10931135 * @return string Converted content with 'srcset' and 'sizes' attributes added to images.
    function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width 
    10951137function wp_make_content_images_responsive( $content ) {
    10961138        $images = get_media_embedded_in_content( $content, 'img' );
    10971139
    1098         $attachment_ids = array();
     1140        $selected_images = array();
    10991141
    11001142        foreach( $images as $image ) {
    1101                 if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) {
    1102                         $attachment_id = (int) $class_id[1];
    1103                         if ( $attachment_id ) {
    1104                                 $attachment_ids[] = $attachment_id;
    1105                         }
     1143                if ( false === strpos( $image, ' srcset="' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) &&
     1144                        ( $attachment_id = absint( $class_id[1] ) ) ) {
     1145
     1146                        $selected_images[$image] = $attachment_id;
    11061147                }
    11071148        }
    11081149
    1109         if ( 0 < count( $attachment_ids ) ) {
     1150        if ( count( $selected_images ) > 1 ) {
    11101151                /*
    1111                  * Warm object caches for use with wp_get_attachment_metadata.
     1152                 * Warm object cache for use with get_post_meta().
    11121153                 *
    11131154                 * To avoid making a database call for each image, a single query
    11141155                 * warms the object cache with the meta information for all images.
    11151156                 */
    1116                 _prime_post_caches( $attachment_ids, false, true );
     1157                update_meta_cache( 'post', array_unique( array_values( ( $selected_images ) ) ) );
    11171158        }
    11181159
    1119         foreach( $images as $image ) {
    1120                 $content = str_replace( $image, wp_img_add_srcset_and_sizes( $image ), $content );
     1160        foreach ( $selected_images as $image => $attachment_id ) {
     1161                $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
     1162                $content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content );
    11211163        }
    11221164
    11231165        return $content;
    function wp_make_content_images_responsive( $content ) { 
    11311173 * @see wp_get_attachment_image_srcset()
    11321174 * @see wp_get_attachment_image_sizes()
    11331175 *
    1134  * @param string $image An HTML 'img' element to be filtered.
     1176 * @param string $image         An HTML 'img' element to be filtered.
     1177 * @param array  $image_meta    The image meta data.
     1178 * @param int    $attachment_id Image attachment ID.
    11351179 * @return string Converted 'img' element with `srcset` and `sizes` attributes added.
    11361180 */
    1137 function wp_img_add_srcset_and_sizes( $image ) {
    1138         // Return early if a 'srcset' attribute already exists.
    1139         if ( false !== strpos( $image, ' srcset="' ) ) {
     1181function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
     1182        // Ensure the image meta exists
     1183        if ( empty( $image_meta['sizes'] ) ) {
    11401184                return $image;
    11411185        }
    11421186
    1143         // Parse id, size, width, and height from the `img` element.
    1144         $id     = preg_match( '/wp-image-([0-9]+)/i', $image, $match_id     ) ? (int) $match_id[1]     : false;
    1145         $size   = preg_match( '/size-([^\s|"]+)/i',   $image, $match_size   ) ? $match_size[1]         : false;
    1146         $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : false;
     1187        $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : '';
     1188        list( $src ) = explode( '?', $src );
    11471189
    1148         if ( $id && ! $size ) {
    1149                 $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : false;
    1150 
    1151                 if ( $width && $height ) {
    1152                         $size = array( $width, $height );
    1153                 }
     1190        // Return early if we coudn't get the image source.
     1191        if ( ! $src ) {
     1192                return $image;
    11541193        }
    11551194
    1156         /*
    1157          * If attempts to parse the size value failed, attempt to use the image
    1158          * metadata to match the 'src' against the available sizes for an attachment.
    1159          */
    1160         if ( $id && ! $size ) {
    1161                 $meta = wp_get_attachment_metadata( $id );
     1195        // Bail early when an image has been inserted and later edited.
     1196        // We don't have the previous image meta to generate srcset.
     1197        if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) &&
     1198                strpos( wp_basename( $src ), $img_edit_hash[0] ) === false ) {
    11621199
    1163                 // Parse the image src value from the img element.
    1164                 $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false;
     1200                return $image;
     1201        }
    11651202
    1166                 // Return early if the metadata does not exist or the src value is empty.
    1167                 if ( ! $meta || ! $src ) {
    1168                         return $image;
    1169                 }
     1203        $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : 0;
     1204        $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
    11701205
     1206        if ( ! $width || ! $height ) {
    11711207                /*
    1172                  * First, see if the file is the full size image. If not, loop through
    1173                  * the intermediate sizes until we find a file that matches.
     1208                 * If attempts to parse the size value failed, attempt to use the image
     1209                 * metadata to match the 'src' against the available sizes for an attachment.
    11741210                 */
    11751211                $image_filename = wp_basename( $src );
    11761212
    1177                 if ( $image_filename === basename( $meta['file'] ) ) {
    1178                         $size = 'full';
     1213                if ( $image_filename === basename( $image_meta['file'] ) ) {
     1214                        $width = (int) $image_meta['width'];
     1215                        $height = (int) $image_meta['height'];
    11791216                } else {
    1180                         foreach( $meta['sizes'] as $image_size => $image_size_data ) {
     1217                        foreach( $image_meta['sizes'] as $image_size_data ) {
    11811218                                if ( $image_filename === $image_size_data['file'] ) {
    1182                                         $size = $image_size;
     1219                                        $width = (int) $image_size_data['width'];
     1220                                        $height = (int) $image_size_data['height'];
    11831221                                        break;
    11841222                                }
    11851223                        }
    11861224                }
    1187 
    11881225        }
    11891226
    1190         // If ID and size exist, try for 'srcset' and 'sizes' and update the markup.
    1191         if ( $id && $size ) {
    1192                 $srcset = wp_get_attachment_image_srcset( $id, $size );
    1193                 $sizes  = wp_get_attachment_image_sizes( $id, $size, $width );
     1227        if ( ! $width || ! $height ) {
     1228                return $image;
     1229        }
    11941230
    1195                 if ( $srcset && $sizes ) {
    1196                         // Format the srcset and sizes string and escape attributes.
    1197                         $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes) );
     1231        $size_array = array( $width, $height );
     1232        $sources = _wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id );
    11981233
    1199                         // Add srcset and sizes attributes to the image markup.
    1200                         $image = preg_replace( '/<img ([^>]+)[\s?][\/?]>/', '<img $1' . $srcset_and_sizes . ' />', $image );
     1234        // Only calculate srcset and sizes values if there is more than one source.
     1235        $srcset = $sizes = false;
     1236        if ( count( $sources ) > 1 ) {
     1237                $srcset = '';
     1238                foreach ( $sources as $source ) {
     1239                        $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
    12011240                }
     1241
     1242                /**
     1243                 * Filter the output of wp_get_attachment_image_srcset().
     1244                 *
     1245                 * @since 4.4.0
     1246                 *
     1247                 * @see wp_get_attachment_image_srcset()
     1248                 *
     1249                 * @param string       $srcset        A source set formatted for a `srcset` attribute.
     1250                 * @param int          $attachment_id Image attachment ID.
     1251                 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
     1252                 *                                    values in pixels (in that order). Default 'medium'.
     1253                 * @param array        $image_meta    The image meta data.
     1254                 */
     1255                $srcset = apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size_array, $image_meta );
     1256
     1257                $sizes = wp_get_attachment_image_sizes( $attachment_id, $size_array, $image_meta );
     1258        }
     1259
     1260
     1261        if ( $srcset && $sizes ) {
     1262                // Format the srcset and sizes string and escape attributes.
     1263                $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes ) );
     1264
     1265                // Add srcset and sizes attributes to the image markup.
     1266                $image = preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $srcset_and_sizes . ' />', $image );
    12021267        }
    12031268
    12041269        return $image;
  • tests/phpunit/tests/media.php

    diff --git tests/phpunit/tests/media.php tests/phpunit/tests/media.php
    index b682b09..29e9da4 100644
    EOF; 
    716716        }
    717717
    718718        /**
     719         * Helper function to get image size array from size "name"
     720         */
     721        function _get_image_size_array_from_name( $size_name ) {
     722                switch ( $size_name ) {
     723                        case 'thumbnail':
     724                                return array( 150, 150 );
     725                        case 'medium':
     726                                return array( 300, 225 );
     727                        case 'large':
     728                                return array( 1024, 768 );
     729                        case 'full':
     730                                return array( 1600, 1200 ); // actual size of ../data/images/test-image-large.png
     731                        default:
     732                                return array( 800, 600 ); // soft-resized image
     733                }
     734        }
     735
     736        /**
    719737         * @ticket 33641
    720738         */
    721         function test_wp_get_attachment_image_srcset_array() {
     739        function test_wp_calculate_image_srcset() {
    722740                $year_month = date('Y/m');
    723                 $image = wp_get_attachment_metadata( self::$large_id );
     741                $image_meta = wp_get_attachment_metadata( self::$large_id );
    724742
    725743                $expected = array(
    726744                        array(
    727                                 'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image['sizes']['medium']['file'],
     745                                'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image_meta['sizes']['medium']['file'],
    728746                                'descriptor' => 'w',
    729                                 'value'      => $image['sizes']['medium']['width'],
     747                                'value'      => $image_meta['sizes']['medium']['width'],
    730748                        ),
    731749                        array(
    732                                 'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image['sizes']['large']['file'],
     750                                'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image_meta['sizes']['large']['file'],
    733751                                'descriptor' => 'w',
    734                                 'value'      => $image['sizes']['large']['width'],
     752                                'value'      => $image_meta['sizes']['large']['width'],
    735753                        ),
    736754                        array(
    737                                 'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'],
     755                                'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'],
    738756                                'descriptor' => 'w',
    739                                 'value'      => $image['width'],
     757                                'value'      => $image_meta['width'],
    740758                        ),
    741759                );
    742760
    EOF; 
    744762                $sizes = array( 'medium', 'large', 'full', 'yoav' );
    745763
    746764                foreach ( $sizes as $size ) {
    747                         $this->assertSame( $expected, wp_get_attachment_image_srcset_array( self::$large_id, $size ) );
     765                        $image_url = wp_get_attachment_image_url( self::$large_id, $size );
     766                        $size_array = $this->_get_image_size_array_from_name( $size );
     767                        $this->assertSame( $expected, _wp_calculate_image_srcset( self::$large_id, $size_array, $image_meta ) );
    748768                }
    749769        }
    750770
    751771        /**
    752772         * @ticket 33641
    753773         */
    754         function test_wp_get_attachment_image_srcset_array_no_date_uploads() {
     774        function test_wp_calculate_image_srcset_no_date_uploads() {
    755775                // Save the current setting for uploads folders
    756776                $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' );
    757777
    EOF; 
    762782                $filename = DIR_TESTDATA . '/images/test-image-large.png';
    763783                $id = self::factory()->attachment->create_upload_object( $filename );
    764784
    765                 $image = wp_get_attachment_metadata( $id );
     785                $image_meta = wp_get_attachment_metadata( $id );
    766786
    767787                $expected = array(
    768788                        array(
    769                                 'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['sizes']['medium']['file'],
     789                                'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['sizes']['medium']['file'],
    770790                                'descriptor' => 'w',
    771                                 'value'      => $image['sizes']['medium']['width'],
     791                                'value'      => $image_meta['sizes']['medium']['width'],
    772792                        ),
    773793                        array(
    774                                 'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['sizes']['large']['file'],
     794                                'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['sizes']['large']['file'],
    775795                                'descriptor' => 'w',
    776                                 'value'      => $image['sizes']['large']['width'],
     796                                'value'      => $image_meta['sizes']['large']['width'],
    777797                        ),
    778798                        array(
    779                                 'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'],
     799                                'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'],
    780800                                'descriptor' => 'w',
    781                                 'value'      => $image['width'],
     801                                'value'      => $image_meta['width'],
    782802                        ),
    783803                );
    784804
    EOF; 
    786806                $sizes = array( 'medium', 'large', 'full', 'yoav' );
    787807
    788808                foreach ( $sizes as $size ) {
    789                         $this->assertSame( $expected, wp_get_attachment_image_srcset_array( $id, $size ) );
     809                        $size_array = $this->_get_image_size_array_from_name( $size );
     810                        $image_url = wp_get_attachment_image_url( self::$large_id, $size );
     811                        $this->assertSame( $expected, _wp_calculate_image_srcset( $image_url, $size_array, $image_meta ) );
    790812                }
    791813
    792814                // Leave the uploads option the way you found it.
    EOF; 
    796818        /**
    797819         * @ticket 33641
    798820         */
    799         function test_wp_get_attachment_image_srcset_array_with_edits() {
     821        function test_wp_calculate_image_srcset_with_edits() {
    800822                // For this test we're going to mock metadata changes from an edit.
    801823                // Start by getting the attachment metadata.
    802                 $meta = wp_get_attachment_metadata( self::$large_id );
     824                $image_meta = wp_get_attachment_metadata( self::$large_id );
     825                $image_url = wp_get_attachment_image_url( self::$large_id );
     826                $size_array = $this->_get_image_size_array_from_name( 'medium' );
    803827
    804828                // Copy hash generation method used in wp_save_image().
    805829                $hash = 'e' . time() . rand(100, 999);
    806830
    807831                // Replace file paths for full and medium sizes with hashed versions.
    808                 $filename_base = basename( $meta['file'], '.png' );
    809                 $meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['file'] );
    810                 $meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['sizes']['medium']['file'] );
    811 
    812                 // Save edited metadata.
    813                 wp_update_attachment_metadata( self::$large_id, $meta );
     832                $filename_base = basename( $image_meta['file'], '.png' );
     833                $image_meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['file'] );
     834                $image_meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['sizes']['medium']['file'] );
     835                $image_meta['sizes']['large']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['sizes']['large']['file'] );
    814836
    815837                // Calculate a srcset array.
    816                 $sizes = wp_get_attachment_image_srcset_array( self::$large_id, 'medium' );
     838                $sizes = _wp_calculate_image_srcset( $image_url, $size_array, $image_meta );
    817839
    818840                // Test to confirm all sources in the array include the same edit hash.
    819841                foreach ( $sizes as $size ) {
    EOF; 
    824846        /**
    825847         * @ticket 33641
    826848         */
    827         function test_wp_get_attachment_image_srcset_array_false() {
    828                 $sizes = wp_get_attachment_image_srcset_array( 99999, 'foo' );
     849        function test_wp_calculate_image_srcset_false() {
     850                $sizes = _wp_calculate_image_srcset( 'file.png', array( 400, 300 ), array() );
    829851
    830852                // For canola.jpg we should return
    831853                $this->assertFalse( $sizes );
    EOF; 
    834856        /**
    835857         * @ticket 33641
    836858         */
    837         function test_wp_get_attachment_image_srcset_array_no_width() {
    838                 // Filter image_downsize() output.
    839                 add_filter( 'wp_generate_attachment_metadata', array( $this, '_test_wp_get_attachment_image_srcset_array_no_width_filter' ) );
    840 
    841                 $old_meta = get_post_meta( self::$large_id, '_wp_attachment_metadata', true );
     859        function test_wp_calculate_image_srcset_no_width() {
    842860                $file = get_attached_file( self::$large_id );
     861                $image_url = wp_get_attachment_image_url( self::$large_id, 'medium' );
     862                $image_meta = wp_generate_attachment_metadata( self::$large_id, $file );
    843863
    844                 $data = wp_generate_attachment_metadata( self::$large_id, $file );
    845                 wp_update_attachment_metadata( self::$large_id, $data );
    846 
    847                 $srcset = wp_get_attachment_image_srcset_array( self::$large_id, 'medium' );
     864                $size_array = array(0, 0);
    848865
    849                 update_post_meta( self::$large_id, '_wp_attachment_metadata', $old_meta );
     866                $srcset = _wp_calculate_image_srcset( $image_url, $size_array, $image_meta );
    850867
    851868                // The srcset should be false.
    852869                $this->assertFalse( $srcset );
    853870        }
    854871
    855872        /**
    856          * Helper function to filter image_downsize and return zero values for width and height.
    857          */
    858         public function _test_wp_get_attachment_image_srcset_array_no_width_filter( $meta ) {
    859                 remove_filter( 'wp_generate_attachment_metadata', array( $this, __FUNCTION__ ) );
    860 
    861                 $meta['sizes']['medium']['width'] = 0;
    862                 $meta['sizes']['medium']['height'] = 0;
    863                 return $meta;
    864         }
    865 
    866         /**
    867873         * @ticket 33641
    868874         */
    869875        function test_wp_get_attachment_image_srcset() {
    870                 $sizes = wp_get_attachment_image_srcset( self::$large_id, 'full-size' );
     876                $image_meta = wp_get_attachment_metadata( self::$large_id );
     877                $size_array = array( 1600, 1200 ); // full size
     878
     879                $sizes = wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta );
    871880
    872                 $image = wp_get_attachment_metadata( self::$large_id );
    873881                $year_month = date('Y/m');
    874882
    875883                $expected = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month = date('Y/m') . '/'
    876                         . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w, ';
     884                        . $image_meta['sizes']['medium']['file'] . ' ' . $image_meta['sizes']['medium']['width'] . 'w, ';
    877885                $expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month = date('Y/m') . '/'
    878                         . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w, ';
    879                 $expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w';
     886                        . $image_meta['sizes']['large']['file'] . ' ' . $image_meta['sizes']['large']['width'] . 'w, ';
     887                $expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w';
    880888
    881889                $this->assertSame( $expected, $sizes );
    882890        }
    EOF; 
    885893         * @ticket 33641
    886894         */
    887895        function test_wp_get_attachment_image_srcset_single_srcset() {
     896                $image_meta = wp_get_attachment_metadata( self::$large_id );
     897                $size_array = array( 150, 150 );
    888898                /*
    889899                 * In our tests, thumbnails will only return a single srcset candidate,
    890900                 * so we shouldn't return a srcset value in order to avoid unneeded markup.
    891901                 */
    892                 $sizes = wp_get_attachment_image_srcset( self::$large_id, 'thumbnail' );
     902                $sizes = wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta );
    893903
    894904                $this->assertFalse( $sizes );
    895905        }
    EOF; 
    899909         */
    900910        function test_wp_get_attachment_image_sizes() {
    901911                // Test sizes against the default WP sizes.
    902                 $intermediates = array('thumbnail', 'medium', 'large');
     912                $intermediates = array( 'thumbnail', 'medium', 'large' );
     913                $image_meta = wp_get_attachment_metadata( self::$large_id );
    903914
    904                 foreach( $intermediates as $int ) {
    905                         $width = get_option( $int . '_size_w' );
     915                foreach( $intermediates as $int_size ) {
     916                        $size_array = $this->_get_image_size_array_from_name( $int_size );
     917                        list( $width, $height ) = $size_array;
    906918
    907919                        $expected = '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
    908                         $sizes = wp_get_attachment_image_sizes( self::$large_id, $int );
     920                        $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta );
    909921
    910                         $this->assertSame($expected, $sizes);
     922                        $this->assertSame( $expected, $sizes );
    911923                }
    912924        }
    913925
    914926        /**
    915927         * @ticket 33641
    916928         */
    917         function test_wp_get_attachment_image_sizes_with_width() {
    918                 $width = 350;
    919 
    920                 $expected = '(max-width: 350px) 100vw, 350px';
    921                 $sizes = wp_get_attachment_image_sizes( self::$large_id, 'medium', $width );
    922 
    923                 $this->assertSame( $expected, $sizes );
    924         }
    925 
    926         /**
    927          * @ticket 33641
    928          */
    929929        function test_wp_make_content_images_responsive() {
    930                 $srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, 'medium' ) );
    931                 $sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( self::$large_id, 'medium' ) );
     930                $image_meta = wp_get_attachment_metadata( self::$large_id );
     931                $size_array = $this->_get_image_size_array_from_name( 'medium' );
     932
     933                $srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta ) );
     934                $sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( self::$large_id, $size_array, $image_meta ) );
    932935
    933936                // Function used to build HTML for the editor.
    934937                $img = get_image_tag( self::$large_id, '', '', '', 'medium' );