Make WordPress Core


Ignore:
Timestamp:
10/06/2015 04:58:21 AM (11 years ago)
Author:
wonderboymusic
Message:

Merge the Responsive Images feature plugin into core, initial commit. See: https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/

New functions in media.php:

  • wp_get_attachment_image_srcset_array() - Returns an array of image candidate string data used to build a srcset value for an attachment given an $attachement_id and $size.
  • wp_get_attachment_image_srcset() - Returns the srcset value for an attachment given an $attachement_id and $size.
  • wp_get_attachment_image_sizes() - Returns the sizes value for an attachment given an $attachement_id and $size and optional arguments used to alter its output.
  • wp_make_content_images_responsive() - A display filter for adding srcset and sizes to images embedded in content.
  • wp_img_add_srcset_and_sizes() - A utility function used by wp_make_content_images_responsive() to add srcset and sizes to a single <img> element.

Modifies existing core functions:

  • Modify wp_get_attachment_image() so the HTML returned for an image includes srcset and sizes.
  • Modify get_media_embedded_in_content() (sup, 3.6 leftover) by adding <img> to the list of accepted tags that can be matched in content. This is used in wp_make_content_images_responsive() to find all of the images embedded in content before passing them off to wp_img_add_srcset_and_sizes().

Tests:

  • Add a new factory method to WP_UnitTest_Factory_For_Attachment named create_upload_object()
  • Adds unit tests
  • Updates unit tests

Props joemcgill, tevko, jaspermdegroot, mdmcginn, barryceelen, peterwilsoncc, fsylum, wonderboymusic, chriscoyier, benjaminpick, jrfnl, #12kingkool68, janhenckens, ryanmarkel, side777, ryelle, wturrell, micahmills, mattbagwell, coliff, DrewAPicture.
See #33641.

File:
1 edited

Legend:

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

    r34851 r34855  
    778778                $attr = wp_parse_args($attr, $default_attr);
    779779
     780                // Generate srcset and sizes if not already present.
     781                if ( empty( $attr['srcset'] ) && $srcset = wp_get_attachment_image_srcset( $attachment_id, $size ) ) {
     782                        $attr['srcset'] = $srcset;
     783                        $sizes_args = array(
     784                                'height' => $height,
     785                                'width'  => $width,
     786                        );
     787                        $attr['sizes'] = wp_get_attachment_image_sizes( $attachment_id, $size, $sizes_args );
     788                }
     789
    780790                /**
    781791                 * Filter the list of attachment image attributes.
     
    813823        $image = wp_get_attachment_image_src( $attachment_id, $size, $icon );
    814824        return isset( $image['0'] ) ? $image['0'] : false;
     825}
     826
     827/**
     828 * Retrieves an array of URLs and pixel widths representing sizes of an image.
     829 *
     830 * The purpose is to populate a source set when creating responsive image markup.
     831 *
     832 * @since 4.4.0
     833 *
     834 * @param  int    $attachment_id Image attachment ID.
     835 * @param  string $size          Optional. Name of image size. Default 'medium'.
     836 * @return array|bool $images {
     837 *     Array image candidate values containing a URL, descriptor type, and
     838 *     descriptor value. False if none exist.
     839 *
     840 *     @type array $values {
     841 *        @type string $url        An image URL.
     842 *        @type string $descriptor A width or density descriptor used in a srcset.
     843 *        @type int    $value      The descriptor value representing a width or
     844 *                                 or pixel density.
     845 *     }
     846 * }
     847 *
     848 */
     849function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' ) {
     850        // Get the intermediate size.
     851        $image = image_get_intermediate_size( $attachment_id, $size );
     852
     853        // Get the post meta.
     854        $img_meta = wp_get_attachment_metadata( $attachment_id );
     855        if ( ! is_array( $img_meta ) ) {
     856                return false;
     857        }
     858
     859        // Extract the height and width from the intermediate or the full size.
     860        $img_width  = ( $image ) ? $image['width']  : $img_meta['width'];
     861        $img_height = ( $image ) ? $image['height'] : $img_meta['height'];
     862
     863        // Bail early if the width isn't greater that zero.
     864        if ( ! $img_width > 0 ) {
     865                return false;
     866        }
     867
     868        // Use the URL from the intermediate size or build the url from the metadata.
     869        if ( ! empty( $image['url'] ) ) {
     870                $img_url = $image['url'];
     871        } else {
     872                $uploads_dir = wp_upload_dir();
     873                $img_file = ( $image ) ? path_join( dirname( $img_meta['file'] ) , $image['file'] ) : $img_meta['file'];
     874                $img_url = $uploads_dir['baseurl'] . '/' . $img_file;
     875        }
     876
     877        $img_sizes = $img_meta['sizes'];
     878
     879        // Add full size to the img_sizes array.
     880        $img_sizes['full'] = array(
     881                'width'  => $img_meta['width'],
     882                'height' => $img_meta['height'],
     883                'file'   => wp_basename( $img_meta['file'] )
     884        );
     885
     886        // Calculate the image aspect ratio.
     887        $img_ratio = $img_height / $img_width;
     888
     889        /*
     890         * Images that have been edited in WordPress after being uploaded will
     891         * contain a unique hash. Look for that hash and use it later to filter
     892         * out images that are leftovers from previous versions.
     893         */
     894        $img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash );
     895
     896        /*
     897         * Set up arrays to hold url candidates and matched image sources so
     898         * we can avoid duplicates without looping through the full sources array
     899         */
     900        $candidates = $sources = array();
     901
     902        /*
     903         * Loop through available images and only use images that are resized
     904         * versions of the same rendition.
     905         */
     906        foreach ( $img_sizes as $img ) {
     907
     908                // Filter out images that are leftovers from previous renditions.
     909                if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) {
     910                        continue;
     911                }
     912
     913                $candidate_url = path_join( dirname( $img_url ), $img['file'] );
     914
     915                // Calculate the new image ratio.
     916                $img_ratio_compare = $img['height'] / $img['width'];
     917
     918                // If the new ratio differs by less than 0.01, use it.
     919                if ( abs( $img_ratio - $img_ratio_compare ) < 0.01 && ! in_array( $candidate_url, $candidates ) ) {
     920                        // Add the URL to our list of candidates.
     921                        $candidates[] = $candidate_url;
     922
     923                        // Add the url, descriptor, and value to the sources array to be returned.
     924                        $sources[] = array(
     925                                'url'        => $candidate_url,
     926                                'descriptor' => 'w',
     927                                'value'      => $img['width'],
     928                        );
     929                }
     930        }
     931
     932        /**
     933         * Filter the output of wp_get_attachment_image_srcset_array().
     934         *
     935         * @since 4.4.0
     936         *
     937         * @param array        $sources       An array of image urls and widths.
     938         * @param int          $attachment_id Attachment ID for image.
     939         * @param array|string $size          Size of image, either array or string.
     940         */
     941        return apply_filters( 'wp_get_attachment_image_srcset_array', $sources, $attachment_id, $size );
     942}
     943
     944/**
     945 * Retrieves the value for an image attachment's 'srcset' attribute.
     946 *
     947 * @since 4.4.0
     948 *
     949 * @param int    $attachment_id Image attachment ID.
     950 * @param string $size          Optional. Name of image size. Default 'medium'.
     951 * @return string|bool A 'srcset' value string or false.
     952 */
     953function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium' ) {
     954        $srcset_array = wp_get_attachment_image_srcset_array( $attachment_id, $size );
     955
     956        // Only return a srcset value if there is more than one source.
     957        if ( count( $srcset_array ) <= 1 ) {
     958                return false;
     959        }
     960
     961        $srcset = '';
     962        foreach ( $srcset_array as $source ) {
     963                $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
     964        }
     965
     966        /**
     967         * Filter the output of wp_get_attachment_image_srcset().
     968         *
     969         * @since 4.4.0
     970         *
     971         * @param string       $srcset        A source set formated for a `srcset` attribute.
     972         * @param int          $attachment_id Attachment ID for image.
     973         * @param array|string $size          Size of image, either array or string.
     974         */
     975        return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size );
     976}
     977
     978/**
     979 * Retrieves a source size attribute for an image from an array of values.
     980 *
     981 * @since 4.4.0
     982 *
     983 * @param int    $attachment_id Image attachment ID.
     984 * @param string $size          Optional. Name of image size. Default value: 'medium'.
     985 * @param array  $args {
     986 *     Optional. Arguments to retrieve attachments.
     987 *
     988 *     @type array|string $sizes An array or string containing of size information.
     989 *     @type int          $width A single width value used in the default `sizes` string.
     990 * }
     991 * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
     992 */
     993function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $args = null ) {
     994
     995        // Try to get the image width from $args before calling image_downsize().
     996        if ( is_array( $args ) && ! empty( $args['width'] ) ) {
     997                $img_width = (int) $args['width'];
     998        } elseif ( $img = image_get_intermediate_size( $attachment_id, $size ) ) {
     999                list( $img_width, $img_height ) = image_constrain_size_for_editor( $img['width'], $img['height'], $size );
     1000        }
     1001
     1002        // Bail early if $image_width isn't set.
     1003        if ( ! $img_width ) {
     1004                return false;
     1005        }
     1006
     1007        // Set the image width in pixels.
     1008        $img_width = $img_width . 'px';
     1009
     1010        // Set up our default values.
     1011        $defaults = array(
     1012                'sizes' => array(
     1013                        array(
     1014                                'size_value' => '100vw',
     1015                                'mq_value'   => $img_width,
     1016                                'mq_name'    => 'max-width'
     1017                        ),
     1018                        array(
     1019                                'size_value' => $img_width
     1020                        ),
     1021                )
     1022        );
     1023
     1024        $args = wp_parse_args( $args, $defaults );
     1025
     1026        /**
     1027         * Filter arguments used to create 'sizes' attribute.
     1028         *
     1029         * @since 4.4.0
     1030         *
     1031         * @param array   $args          An array of arguments used to create a 'sizes' attribute.
     1032         * @param int     $attachment_id Post ID of the original image.
     1033         * @param string  $size          Name of the image size being used.
     1034         */
     1035        $args = apply_filters( 'wp_image_sizes_args', $args, $attachment_id, $size );
     1036
     1037        // If sizes is passed as a string, just use the string.
     1038        if ( is_string( $args['sizes'] ) ) {
     1039                $size_list = $args['sizes'];
     1040
     1041        // Otherwise, breakdown the array and build a sizes string.
     1042        } elseif ( is_array( $args['sizes'] ) ) {
     1043
     1044                $size_list = '';
     1045
     1046                foreach ( $args['sizes'] as $size ) {
     1047
     1048                        // Use 100vw as the size value unless something else is specified.
     1049                        $size_value = ( $size['size_value'] ) ? $size['size_value'] : '100vw';
     1050
     1051                        // If a media length is specified, build the media query.
     1052                        if ( ! empty( $size['mq_value'] ) ) {
     1053
     1054                                $media_length = $size['mq_value'];
     1055
     1056                                // Use max-width as the media condition unless min-width is specified.
     1057                                $media_condition = ( ! empty( $size['mq_name'] ) ) ? $size['mq_name'] : 'max-width';
     1058
     1059                                // If a media_length was set, create the media query.
     1060                                $media_query = '(' . $media_condition . ": " . $media_length . ') ';
     1061
     1062                        } else {
     1063                                // If no media length was set, $media_query is blank.
     1064                                $media_query = '';
     1065                        }
     1066
     1067                        // Add to the source size list string.
     1068                        $size_list .= $media_query . $size_value . ', ';
     1069                }
     1070
     1071                // Remove the trailing comma and space from the end of the string.
     1072                $size_list = substr( $size_list, 0, -2 );
     1073        }
     1074
     1075        // Return the sizes value as $size_list or false.
     1076        return ( $size_list ) ? $size_list : false;
     1077}
     1078
     1079/**
     1080 * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes.
     1081 *
     1082 * @since 4.4.0
     1083 *
     1084 * @see wp_img_add_srcset_and_sizes()
     1085 *
     1086 * @param string $content The raw post content to be filtered.
     1087 * @return string Converted content with 'srcset' and 'sizes' attributes added to images.
     1088 */
     1089function wp_make_content_images_responsive( $content ) {
     1090        $images = get_media_embedded_in_content( $content, 'img' );
     1091
     1092        $attachment_ids = array();
     1093
     1094        foreach( $images as $image ) {
     1095                if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) {
     1096                        $attachment_id = (int) $class_id[1];
     1097                        if ( $attachment_id ) {
     1098                                $attachment_ids[] = $attachment_id;
     1099                        }
     1100                }
     1101        }
     1102
     1103        if ( 0 < count( $attachment_ids ) ) {
     1104                /*
     1105                 * Warm object caches for use with wp_get_attachment_metadata.
     1106                 *
     1107                 * To avoid making a database call for each image, a single query
     1108                 * warms the object cache with the meta information for all images.
     1109                 */
     1110                _prime_post_caches( $attachment_ids, false, true );
     1111        }
     1112
     1113        foreach( $images as $image ) {
     1114                $content = str_replace( $image, wp_img_add_srcset_and_sizes( $image ), $content );
     1115        }
     1116
     1117        return $content;
     1118}
     1119
     1120/**
     1121 * Adds 'srcset' and 'sizes' attributes to an existing 'img' element.
     1122 *
     1123 * @since 4.4.0
     1124 *
     1125 * @see wp_get_attachment_image_srcset()
     1126 * @see wp_get_attachment_image_sizes()
     1127 *
     1128 * @param string $image An HTML 'img' element to be filtered.
     1129 * @return string Converted 'img' element with `srcset` and `sizes` attributes added.
     1130 */
     1131function wp_img_add_srcset_and_sizes( $image ) {
     1132        // Return early if a 'srcset' attribute already exists.
     1133        if ( false !== strpos( $image, ' srcset="' ) ) {
     1134                return $image;
     1135        }
     1136
     1137        // Parse id, size, width, and height from the `img` element.
     1138        $id     = preg_match( '/wp-image-([0-9]+)/i', $image, $match_id     ) ? (int) $match_id[1]     : false;
     1139        $size   = preg_match( '/size-([^\s|"]+)/i',   $image, $match_size   ) ? $match_size[1]         : false;
     1140        $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : false;
     1141        $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : false;
     1142
     1143        if ( $id && false === $size ) {
     1144                $size = array( $width, $height );
     1145        }
     1146
     1147        /*
     1148         * If attempts to parse the size value failed, attempt to use the image
     1149         * metadata to match the 'src' against the available sizes for an attachment.
     1150         */
     1151        if ( ! $size && ! empty( $id ) && is_array( $meta = wp_get_attachment_metadata( $id ) ) ) {
     1152                // Parse the image src value from the img element.
     1153                $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false;
     1154
     1155                // Return early if the src value is empty.
     1156                if ( ! $src ) {
     1157                        return $image;
     1158                }
     1159
     1160                /*
     1161                 * First, see if the file is the full size image. If not, loop through
     1162                 * the intermediate sizes until we find a file that matches.
     1163                 */
     1164                $image_filename = wp_basename( $src );
     1165
     1166                if ( $image_filename === basename( $meta['file'] ) ) {
     1167                        $size = 'full';
     1168                } else {
     1169                        foreach( $meta['sizes'] as $image_size => $image_size_data ) {
     1170                                if ( $image_filename === $image_size_data['file'] ) {
     1171                                        $size = $image_size;
     1172                                        break;
     1173                                }
     1174                        }
     1175                }
     1176
     1177        }
     1178
     1179        // If ID and size, try for 'srcset' and 'sizes' and update the markup.
     1180        if ( $id && $size && $srcset = wp_get_attachment_image_srcset( $id, $size ) ) {
     1181
     1182                /*
     1183                 * Pass the 'height' and 'width' to 'wp_get_attachment_image_sizes()' to avoid
     1184                 * recalculating the image size.
     1185                 */
     1186                $args = array(
     1187                        'height' => $height,
     1188                        'width'  => $width,
     1189                );
     1190
     1191                $sizes = wp_get_attachment_image_sizes( $id, $size, $args );
     1192
     1193                // Format the srcset and sizes string and escape attributes.
     1194                $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes) );
     1195
     1196                // Add srcset and sizes attributes to the image markup.
     1197                $image = preg_replace( '/<img ([^>]+)[\s?][\/?]>/', '<img $1' . $srcset_and_sizes . ' />', $image );
     1198        }
     1199
     1200        return $image;
    8151201}
    8161202
     
    29873373         *
    29883374         * @since 4.2.0
     3375         * @since 4.4.0 Added 'img' to the allowed types.
    29893376         *
    29903377         * @param array $allowed_media_types An array of allowed media types. Default media types are
    2991          *                                   'audio', 'video', 'object', 'embed', and 'iframe'.
    2992          */
    2993         $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe' ) );
     3378         *                                   'audio', 'video', 'object', 'embed', 'iframe', and 'img'.
     3379         */
     3380        $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe', 'img' ) );
    29943381
    29953382        if ( ! empty( $types ) ) {
Note: See TracChangeset for help on using the changeset viewer.