Make WordPress Core

Changeset 35412


Ignore:
Timestamp:
10/28/2015 05:40:04 AM (9 years ago)
Author:
azaozz
Message:

Responsive images:

  • Introduce wp_calculate_image_srcset() that replaces wp_get_attachment_image_srcset_array() and is used as lower level function for retrieving the srcset data as array.
  • Use the new function when generating srcset and sizes on the front-end. This is faster as no (other) image API functions are used.
  • Change the wp_get_attachment_image_srcset(). Now it is meant for use in templates and is no longer used in core.
  • A few logic fixes and improvements.
  • Some names changed to be (hopefully) more descriptive.
  • Fixed/updated tests.

Props joemcgill, jaspermdegroot, azaozz.
See #34430.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/media.php

    r35405 r35412  
    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 );
    818 
    819             if ( $srcset && $sizes ) {
    820                 $attr['srcset'] = $srcset;
    821 
    822                 if ( empty( $attr['sizes'] ) ) {
     816            $image_meta = wp_get_attachment_metadata( $attachment_id );
     817            $size_array = array( absint( $width ), absint( $height ) );
     818            $sources = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id );
     819
     820            if ( count( $sources ) > 1 ) {
     821                $attr['srcset'] = wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id );
     822
     823                if ( empty( $attr['sizes'] ) && ( $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id ) ) ) {
    823824                    $attr['sizes'] = $sizes;
    824825                }
     
    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    return wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id );
     936}
     937
     938
     939/**
     940 * A helper function to concatenate and filter the srcset attribute value.
     941 *
     942 * @since 4.4.0
     943 *
     944 * @param array   $sources       The array containing image sizes data as returned by wp_calculate_image_srcset().
     945 * @param array   $size_array    Array of width and height values in pixels (in that order).
     946 * @param array   $image_meta    The image meta data.
     947 * @param int     $attachment_id The image attachment post id to pass to the filter.
     948 * @return string The srcset attribute value.
     949 */
     950function wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id ) {
     951    $srcset = '';
     952
     953    foreach ( $sources as $source ) {
     954        $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
     955    }
     956
     957    /**
     958     * Filter the output of wp_get_attachment_image_srcset().
     959     *
     960     * @since 4.4.0
     961     *
     962     * @param string       $srcset        A source set formatted for a `srcset` attribute.
     963     * @param int          $attachment_id Image attachment ID.
     964     * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
     965     *                                    values in pixels (in that order). Default 'medium'.
     966     * @param array        $image_meta    The image meta data.
     967     */
     968    return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size_array, $image_meta );
     969}
     970
     971/**
     972 * A helper function to caclulate the image sources to include in a srcset attribute.
     973 *
     974 * @since 4.4.0
     975 *
     976 * @param string $image_name    The file name, path, URL or partial path or URL of the image being matched.
     977 * @param array  $size_array    Array of width and height values in pixels (in that order).
     978 * @param array  $image_meta    The image meta data.
     979 * @param int    $attachment_id Optional. The image attachment post id to pass to the filter.
    876980 * @return array|bool $sources {
    877981 *     Array image candidate values containing a URL, descriptor type, and
     
    887991 *
    888992 */
    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 ) ) {
     993function wp_calculate_image_srcset( $image_name, $size_array, $image_meta, $attachment_id = 0 ) {
     994    if ( empty( $image_meta['sizes'] ) ) {
    896995        return false;
    897996    }
    898997
    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'];
    902 
    903     // Bail early if the width isn't greater than zero.
    904     if ( ! $img_width > 0 ) {
     998    $image_sizes = $image_meta['sizes'];
     999
     1000    // Get the height and width for the image.
     1001    $image_width = (int) $size_array[0];
     1002    $image_height = (int) $size_array[1];
     1003
     1004    // Bail early if error/no width.
     1005    if ( $image_width < 1 ) {
    9051006        return false;
    9061007    }
    9071008
    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;
    915     }
    916 
    917     $img_sizes = $img_meta['sizes'];
    918 
    9191009    // 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'] )
     1010    $image_sizes['full'] = array(
     1011        'width'  => $image_meta['width'],
     1012        'height' => $image_meta['height'],
     1013        'file'   => wp_basename( $image_meta['file'] ),
    9241014    );
    9251015
     1016    $image_baseurl = _wp_upload_dir_baseurl();
     1017    $dirname = dirname( $image_meta['file'] );
     1018
     1019    if ( $dirname !== '.' ) {
     1020        $image_baseurl = path_join( $image_baseurl, $dirname );
     1021    }
     1022
    9261023    // Calculate the image aspect ratio.
    927     $img_ratio = $img_height / $img_width;
     1024    $image_ratio = $image_height / $image_width;
    9281025
    9291026    /*
     
    9321029     * out images that are leftovers from previous versions.
    9331030     */
    934     $img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash );
    935 
    936     /**
    937      * Filter the maximum width included in a srcset attribute.
     1031    $image_edited = preg_match( '/-e[0-9]{13}/', $image_name, $image_edit_hash );
     1032
     1033    /**
     1034     * Filter the maximum width included in a 'srcset' attribute.
    9381035     *
    9391036     * @since 4.4.0
    9401037     *
    941      * @param array|string $size Size of image, either array or string.
    942      */
    943     $max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size );
     1038     * @param int          $max_width  The maximum width to include in the 'srcset'. Default '1600'.
     1039     * @param array|string $size_array Array of width and height values in pixels (in that order).
     1040     */
     1041    $max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size_array );
     1042
     1043    // Array to hold URL candidates.
     1044    $sources = array();
    9441045
    9451046    /*
    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();
    950 
    951     /*
    952      * Loop through available images and only use images that are resized
    953      * versions of the same rendition.
    954      */
    955     foreach ( $img_sizes as $img ) {
    956 
    957         // Filter out images that are leftovers from previous renditions.
    958         if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) {
     1047     * Loop through available images. Only use images that are resized
     1048     * versions of the same edit.
     1049     */
     1050    foreach ( $image_sizes as $image ) {
     1051
     1052        // Filter out images that are from previous edits.
     1053        if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
    9591054            continue;
    9601055        }
    9611056
    9621057        // Filter out images that are wider than $max_srcset_width.
    963         if ( $max_srcset_width && $img['width'] > $max_srcset_width ) {
     1058        if ( $max_srcset_width && $image['width'] > $max_srcset_width ) {
    9641059            continue;
    9651060        }
    9661061
    967         $candidate_url = path_join( dirname( $img_url ), $img['file'] );
     1062        $candidate_url = $image['file'];
    9681063
    9691064        // Calculate the new image ratio.
    970         if ( $img['width'] ) {
    971             $img_ratio_compare = $img['height'] / $img['width'];
     1065        if ( $image['width'] ) {
     1066            $image_ratio_compare = $image['height'] / $image['width'];
    9721067        } else {
    973             $img_ratio_compare = 0;
     1068            $image_ratio_compare = 0;
    9741069        }
    9751070
    9761071        // 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,
     1072        if ( abs( $image_ratio - $image_ratio_compare ) < 0.01 && ! array_key_exists( $candidate_url, $sources ) ) {
     1073            // Add the URL, descriptor, and value to the sources array to be returned.
     1074            $sources[ $image['width'] ] = array(
     1075                'url'        => path_join( $image_baseurl, $candidate_url ),
    9841076                'descriptor' => 'w',
    985                 'value'      => $img['width'],
     1077                'value'      => $image['width'],
    9861078            );
    9871079        }
     
    9931085     * @since 4.4.0
    9941086     *
    995      * @param array        $sources       An array of image urls and widths.
     1087     * @param array        $sources       An array of image URLs and widths.
    9961088     * @param int          $attachment_id Attachment ID for image.
    9971089     * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    9981090     *                                    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.
     1091     * @param array        $image_meta    The image meta data.
     1092
     1093     */
     1094    return apply_filters( 'wp_get_attachment_image_srcset_array', array_values( $sources ), $attachment_id, $size_array, $image_meta );
     1095}
     1096
     1097/**
     1098 * Create `sizes` attribute value for an image.
    10051099 *
    10061100 * @since 4.4.0
    10071101 *
    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 ) {
     1102 * @param array|string $size          Image size. Accepts any valid image size name (thumbnail, medium, etc.),
     1103 *                                    or an array of width and height values in pixels (in that order).
     1104 * @param array        $image_meta    Optional. The image meta data.
     1105 * @param int          $attachment_id Optional. Image attachment ID. Either $image_meta or $attachment_id is needed
     1106 *                                    when using image size name.
     1107 *
     1108 * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
     1109 */
     1110function wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment_id = 0 ) {
     1111    $width = 0;
     1112
     1113    if ( is_numeric( $size ) ) {
     1114        $width = absint( $size );
     1115    } elseif ( is_array( $size ) ) {
     1116        $width = absint( $size[0] );
     1117    } elseif ( is_string( $size ) ) {
     1118        if ( ! $image_meta && $attachment_id ) {
     1119            $image_meta = wp_get_attachment_metadata( $attachment_id );
     1120        }
     1121
     1122        if ( $image_meta ) {
     1123            $width = _wp_get_image_size_from_meta( $size, $image_meta );
     1124            if ( $width ) {
     1125                $width = $width[0];
     1126            }
     1127        }
     1128    }
     1129
     1130    if ( ! $width ) {
    10181131        return false;
    10191132    }
    10201133
    1021     $srcset = '';
    1022     foreach ( $srcset_array as $source ) {
    1023         $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
    1024     }
    1025 
    1026     /**
    1027      * Filter the output of wp_get_attachment_image_srcset().
     1134    // Setup the default sizes attribute.
     1135    $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', (int) $width );
     1136
     1137    /**
     1138     * Filter the output of wp_get_attachment_image_sizes().
    10281139     *
    10291140     * @since 4.4.0
    10301141     *
    1031      * @param string       $srcset        A source set formated for a `srcset` attribute.
    1032      * @param int          $attachment_id Attachment ID for image.
     1142     * @param string       $sizes         A source size value for use in a 'sizes' attribute.
    10331143     * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    10341144     *                                    values in pixels (in that order). Default 'medium'.
    1035      */
    1036     return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size );
    1037 }
    1038 
    1039 /**
    1040  * Retrieves a source size attribute for an image from an array of values.
     1145     * @param array        $image_meta    The image meta data.
     1146     * @param int          $attachment_id Post ID of the original image.
     1147     */
     1148    return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $size, $image_meta, $attachment_id );
     1149}
     1150
     1151/**
     1152 * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes.
    10411153 *
    10421154 * @since 4.4.0
    10431155 *
    1044  * @param int          $attachment_id Image attachment ID.
    1045  * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    1046  *                                    values in pixels (in that order). Default 'medium'.
    1047  * @param int          $width         Optional. Display width of the image.
    1048  * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
    1049  */
    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.
    1055     } 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;
    1061     }
    1062 
    1063     // Bail early if $img_width isn't set.
    1064     if ( ! $img_width ) {
    1065         return false;
    1066     }
    1067 
    1068     // Setup the default sizes attribute.
    1069     $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $img_width );
    1070 
    1071     /**
    1072      * Filter the output of wp_get_attachment_image_sizes().
    1073      *
    1074      * @since 4.4.0
    1075      *
    1076      * @param string       $sizes         A source size value for use in a 'sizes' attribute.
    1077      * @param int          $attachment_id Post ID of the original image.
    1078      * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
    1079      *                                    values in pixels (in that order). Default 'medium'.
    1080      * @param int          $width         Display width of the image.
    1081      */
    1082     return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $attachment_id, $size, $width );
    1083 }
    1084 
    1085 /**
    1086  * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes.
    1087  *
    1088  * @since 4.4.0
    1089  *
    1090  * @see wp_img_add_srcset_and_sizes()
     1156 * @see wp_image_add_srcset_and_sizes()
    10911157 *
    10921158 * @param string $content The raw post content to be filtered.
     
    10961162    $images = get_media_embedded_in_content( $content, 'img' );
    10971163
    1098     $attachment_ids = array();
     1164    $selected_images = $attachment_ids = array();
    10991165
    11001166    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             }
    1106         }
    1107     }
    1108 
    1109     if ( 0 < count( $attachment_ids ) ) {
     1167        if ( false === strpos( $image, ' srcset="' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) &&
     1168            ( $attachment_id = absint( $class_id[1] ) ) ) {
     1169
     1170            // If exactly the same image tag is used more than once, overwrite it.
     1171            // All identical tags will be replaced later with str_replace().
     1172            $selected_images[ $image ] = $attachment_id;
     1173            // Overwrite the ID when the same image is included more than once.
     1174            $attachment_ids[ $attachment_id ] = true;
     1175        }
     1176    }
     1177
     1178    if ( count( $attachment_ids ) > 1 ) {
    11101179        /*
    1111          * Warm object caches for use with wp_get_attachment_metadata.
     1180         * Warm object cache for use with get_post_meta().
    11121181         *
    11131182         * To avoid making a database call for each image, a single query
    11141183         * warms the object cache with the meta information for all images.
    11151184         */
    1116         _prime_post_caches( $attachment_ids, false, true );
    1117     }
    1118 
    1119     foreach( $images as $image ) {
    1120         $content = str_replace( $image, wp_img_add_srcset_and_sizes( $image ), $content );
     1185        update_meta_cache( 'post', array_keys( $attachment_ids ) );
     1186    }
     1187
     1188    foreach ( $selected_images as $image => $attachment_id ) {
     1189        $image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
     1190        $content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content );
    11211191    }
    11221192
     
    11321202 * @see wp_get_attachment_image_sizes()
    11331203 *
    1134  * @param string $image An HTML 'img' element to be filtered.
     1204 * @param string $image         An HTML 'img' element to be filtered.
     1205 * @param array  $image_meta    The image meta data.
     1206 * @param int    $attachment_id Image attachment ID.
    11351207 * @return string Converted 'img' element with `srcset` and `sizes` attributes added.
    11361208 */
    1137 function wp_img_add_srcset_and_sizes( $image ) {
    1138     // Return early if a 'srcset' attribute already exists.
    1139     if ( false !== strpos( $image, ' srcset="' ) ) {
     1209function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
     1210    // Ensure the image meta exists
     1211    if ( empty( $image_meta['sizes'] ) ) {
    11401212        return $image;
    11411213    }
    11421214
    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;
    1147 
    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         }
    1154     }
    1155 
    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 );
    1162 
    1163         // Parse the image src value from the img element.
    1164         $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false;
    1165 
    1166         // Return early if the metadata does not exist or the src value is empty.
    1167         if ( ! $meta || ! $src ) {
    1168             return $image;
    1169         }
    1170 
     1215    $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : '';
     1216    list( $src ) = explode( '?', $src );
     1217
     1218    // Return early if we coudn't get the image source.
     1219    if ( ! $src ) {
     1220        return $image;
     1221    }
     1222
     1223    // Bail early when an image has been inserted and later edited.
     1224    if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) &&
     1225        strpos( wp_basename( $src ), $img_edit_hash[0] ) === false ) {
     1226
     1227        return $image;
     1228    }
     1229
     1230    $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : 0;
     1231    $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
     1232
     1233    if ( ! $width || ! $height ) {
    11711234        /*
    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.
     1235         * If attempts to parse the size value failed, attempt to use the image
     1236         * metadata to match the image file name from 'src' against the available sizes for an attachment.
    11741237         */
    11751238        $image_filename = wp_basename( $src );
    11761239
    1177         if ( $image_filename === basename( $meta['file'] ) ) {
    1178             $size = 'full';
     1240        if ( $image_filename === wp_basename( $image_meta['file'] ) ) {
     1241            $width = (int) $image_meta['width'];
     1242            $height = (int) $image_meta['height'];
    11791243        } else {
    1180             foreach( $meta['sizes'] as $image_size => $image_size_data ) {
     1244            foreach( $image_meta['sizes'] as $image_size_data ) {
    11811245                if ( $image_filename === $image_size_data['file'] ) {
    1182                     $size = $image_size;
     1246                    $width = (int) $image_size_data['width'];
     1247                    $height = (int) $image_size_data['height'];
    11831248                    break;
    11841249                }
    11851250            }
    11861251        }
    1187 
    1188     }
    1189 
    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 );
    1194 
    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) );
    1198 
    1199             // Add srcset and sizes attributes to the image markup.
    1200             $image = preg_replace( '/<img ([^>]+)[\s?][\/?]>/', '<img $1' . $srcset_and_sizes . ' />', $image );
    1201         }
     1252    }
     1253
     1254    if ( ! $width || ! $height ) {
     1255        return $image;
     1256    }
     1257
     1258    $size_array = array( $width, $height );
     1259    $sources = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id );
     1260
     1261    $srcset = $sizes = '';
     1262    // Only calculate srcset and sizes values if there is more than one source.
     1263    if ( count( $sources ) > 1 ) {
     1264        $srcset = wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id );
     1265        $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id );
     1266    }
     1267
     1268    if ( $srcset && $sizes ) {
     1269        // Format the srcset and sizes string and escape attributes.
     1270        $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes ) );
     1271
     1272        // Add srcset and sizes attributes to the image markup.
     1273        $image = preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $srcset_and_sizes . ' />', $image );
    12021274    }
    12031275
  • trunk/tests/phpunit/tests/media.php

    r35355 r35412  
    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        );
     
    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( $image_url, $size_array, $image_meta ) );
    748768        }
    749769    }
     
    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' );
     
    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        );
     
    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
     
    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().
     
    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.
     
    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
     
    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 );
    843 
    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' );
    848 
    849         update_post_meta( self::$large_id, '_wp_attachment_metadata', $old_meta );
     861        $image_url = wp_get_attachment_image_url( self::$large_id, 'medium' );
     862        $image_meta = wp_generate_attachment_metadata( self::$large_id, $file );
     863
     864        $size_array = array(0, 0);
     865
     866        $srcset = wp_calculate_image_srcset( $image_url, $size_array, $image_meta );
    850867
    851868        // The srcset should be false.
     
    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' );
    871 
    872         $image = wp_get_attachment_metadata( self::$large_id );
     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 );
     880
    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 );
     
    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 );
     
    900910    function test_wp_get_attachment_image_sizes() {
    901911        // Test sizes against the default WP sizes.
    902         $intermediates = array('thumbnail', 'medium', 'large');
    903 
    904         foreach( $intermediates as $int ) {
    905             $width = get_option( $int . '_size_w' );
     912        $intermediates = array( 'thumbnail', 'medium', 'large' );
     913        $image_meta = wp_get_attachment_metadata( self::$large_id );
     914
     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 );
    909 
    910             $this->assertSame($expected, $sizes);
     920            $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta );
     921
     922            $this->assertSame( $expected, $sizes );
    911923        }
    912924    }
     
    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( $size_array, $image_meta, self::$large_id ) );
    932935
    933936        // Function used to build HTML for the editor.
    934937        $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
    935         $img_no_size = str_replace( 'size-', '', $img );
    936         $img_no_size_id = str_replace( 'wp-image-', 'id-', $img_no_size );
     938        $img_no_size_in_class = str_replace( 'size-', '', $img );
     939        $img_no_width_height = str_replace( ' width="' . $size_array[0] . '"', '', $img );
     940        $img_no_width_height = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height );
     941        $img_no_size_id = str_replace( 'wp-image-', 'id-', $img );
    937942
    938943        // Manually add srcset and sizes to the markup from get_image_tag();
    939         $respimg = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img);
    940         $respimg_no_size = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size);
    941 
    942         $content = '<p>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.</p>
    943             <p>First things first:</p>
    944 
     944        $respimg = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img );
     945        $respimg_no_size_in_class = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size_in_class );
     946        $respimg_no_width_height = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_width_height );
     947
     948        $content = '
     949            <p>Image, standard. Should have srcset and sizes.</p>
    945950            %1$s
    946951
    947             <ul>
    948             <li><a href="http://wordpress.org" title="Subscribe to the WordPress mailing list for Release Notifications">Subscribe to the WordPress mailing list for release notifications</a></li>
    949             </ul>
    950 
     952            <p>Image, no size class. Should have srcset and sizes.</p>
    951953            %2$s
    952954
    953             <p>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.<br />
    954             When a new version is released, <a href="http://wordpress.org" title="If you are already logged in, this will take you directly to the Dashboard">log in to the Dashboard</a> and follow the instructions.<br />
    955             Upgrading is a couple of clicks!</p>
    956 
     955            <p>Image, no width and height attributes. Should have srcset and sizes (from matching the file name).</p>
    957956            %3$s
    958957
    959             <p>Then you can start enjoying the WordPress experience:</p>
    960             <ul>
    961             <li>Edit your personal information at <a href="http://wordpress.org" title="Edit settings like your password, your display name and your contact information">Users &#8250; Your Profile</a></li>
    962             <li>Start publishing at <a href="http://wordpress.org" title="Create a new post">Posts &#8250; Add New</a> and at <a href="http://wordpress.org" title="Create a new page">Pages &#8250; Add New</a></li>
    963             <li>Browse and install plugins at <a href="http://wordpress.org" title="Browse and install plugins at the official WordPress repository directly from your Dashboard">Plugins &#8250; Add New</a></li>
    964             <li>Browse and install themes at <a href="http://wordpress.org" title="Browse and install themes at the official WordPress repository directly from your Dashboard">Appearance &#8250; Add New Themes</a></li>
    965             <li>Modify and prettify your website&#8217;s links at <a href="http://wordpress.org" title="For example, select a link structure like: http://example.com/1999/12/post-name">Settings &#8250; Permalinks</a></li>
    966             <li>Import content from another system or WordPress site at <a href="http://wordpress.org" title="WordPress comes with importers for the most common publishing systems">Tools &#8250; Import</a></li>
    967             <li>Find answers to your questions at the <a href="http://wordpress.orgs" title="The official WordPress documentation, maintained by the WordPress community">WordPress Codex</a></li>
    968             </ul>';
    969 
    970         $content_unfiltered = sprintf( $content, $img, $img_no_size, $img_no_size_id );
    971         $content_filtered = sprintf( $content, $respimg, $respimg_no_size, $img_no_size_id );
     958            <p>Image, no attachment ID class. Should NOT have srcset and sizes.</p>
     959            %4$s';
     960
     961        $content_unfiltered = sprintf( $content, $img, $img_no_size_in_class, $img_no_width_height, $img_no_size_id );
     962        $content_filtered = sprintf( $content, $respimg, $respimg_no_size_in_class, $respimg_no_width_height, $img_no_size_id );
    972963
    973964        $this->assertSame( $content_filtered, wp_make_content_images_responsive( $content_unfiltered ) );
Note: See TracChangeset for help on using the changeset viewer.