Make WordPress Core

Changeset 53751


Ignore:
Timestamp:
07/21/2022 06:01:01 PM (21 months ago)
Author:
adamsilverstein
Message:

Media: enable generating multiple mime types for image uploads; specifically WebP versions for JPEG images by default.

This changeset adds the capability for core media uploads to generate sub sized images in more than a single mime type. The output formats for each mime type can be controlled through a filter. WebP is used as an additional output format for JPEG images by default to improve front end performance.

When generating additional mime types, only images which are smaller than the respective original are retained. By default, additional mime type images are only generated for the built-in core image sizes and any custom sizes that have opted in.

Image meta is updated with a new 'sources' array containing file details for each mime type. Each image size in the 'sizes' array also gets a new 'sources' array that contains the image file details for each mime type.

This change also increases image upload retries to accommodate additional image sizes. It also adds a $mime_type parameter to the wp_get_missing_image_subsizes function and filter.

This change adds three new filters to enable full control of secondary mime image generation and output:

  • A new filter wp_image_sizes_with_additional_mime_type_support that filters the sizes that support secondary mime type output. Developers can use this to control the output of additional mime type sub-sized images on a per size basis.
  • A new filter wp_upload_image_mime_transforms that filters the output mime types for a given input mime type. Developers can use this to control generation of additional mime types for a given input mime type or even override the original mime type.
  • A new filter wp_content_image_mimes which controls image mime type output selection and order for frontend content. Developers can use this to control the mime type output preference order for content images. Content images inserted from the media library will use the available image versions based on the order from this filter.

Thanks to the many contributors who helped develop, test and give feedback on this feature.

A haiku to summarize:

Upload a JPEG
Images of all sizes
Output as WebPs

Props flixos90, MatthiasReinholz, studiolxv, markhowellsmead, eatingrules, pbiron, mukesh27, joegrainger, mehulkaklotar, tweetythierry, akshitsethi, peterwilsoncc, eugenemanuilov, mitogh, shetheliving, clarkeemily, codekraft, mikeschroder, clorith, kasparsd, spacedmonkey, trevorpfromsandee, jb510, scofennellgmailcom, seedsca, cagsmith, karinclimber, dainemawer, baxbridge, grapplerulrich, sobatkras, chynnabenton, tonylocalword, barneydavey, kwillmorth, garymatthews919, olliejones, imarkinteractive, jeffpaul, feastdesignco, webbeetle, masteradhoc.

See #55443.

Location:
trunk
Files:
3 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/vendor/plupload/handlers.js

    r51947 r53751  
    487487        times = tryAgainCount[ file.id ];
    488488
    489         if ( times && times > 4 ) {
     489        if ( times && times > 8 ) {
    490490            /*
    491491             * The file may have been uploaded and attachment post created,
  • trunk/src/js/_enqueues/vendor/plupload/wp-plupload.js

    r51227 r53751  
    139139            times = tryAgainCount[ file.id ];
    140140
    141             if ( times && times > 4 ) {
     141            if ( times && times > 8 ) {
    142142                /*
    143143                 * The file may have been uploaded and attachment post created,
  • trunk/src/wp-admin/includes/image.php

    r53547 r53751  
    7878 *
    7979 * @since 5.3.0
    80  *
    81  * @param int $attachment_id The image attachment post ID.
     80 * @since 6.1.0 The $mime_type parameter was added.
     81 *
     82 * @param int    $attachment_id The image attachment post ID.
     83 * @param string $mime_type     Optional. The mime type to check for missing sizes. Default is the primary image mime.
    8284 * @return array[] Associative array of arrays of image sub-size information for
    8385 *                 missing image sizes, keyed by image size name.
    8486 */
    85 function wp_get_missing_image_subsizes( $attachment_id ) {
     87function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) {
    8688    if ( ! wp_attachment_is_image( $attachment_id ) ) {
    8789        return array();
     90    }
     91
     92    $primary_mime_type = get_post_mime_type( get_post( $attachment_id ) );
     93    if ( ! $mime_type ) {
     94        $mime_type = $primary_mime_type;
    8895    }
    8996
     
    130137     * as the image may have been used in an older post.
    131138     */
    132     $missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] );
     139    $missing_sizes = array();
     140    foreach ( $possible_sizes as $size_name => $size_data ) {
     141        if ( ! isset( $image_meta['sizes'][ $size_name ] ) ) {
     142            $missing_sizes[ $size_name ] = $size_data;
     143            continue;
     144        }
     145
     146        if ( ( isset( $size_data['mime-type'] ) && $size_data['mime-type'] === $mime_type ) || isset( $size_data['sources'][ $mime_type ] ) ) {
     147            continue;
     148        }
     149
     150        $missing_sizes[ $size_name ] = $size_data;
     151    }
     152
     153    // Filter secondary mime types to those sizes that are enabled.
     154    if ( $primary_mime_type !== $mime_type ) {
     155        $missing_sizes = _wp_filter_image_sizes_additional_mime_type_support( $missing_sizes, $attachment_id );
     156    }
    133157
    134158    /**
     
    136160     *
    137161     * @since 5.3.0
     162     * @since 6.1.0 The $mime_type filter parameter was added.
    138163     *
    139164     * @param array[] $missing_sizes Associative array of arrays of image sub-size information for
     
    141166     * @param array   $image_meta    The image meta data.
    142167     * @param int     $attachment_id The image attachment post ID.
     168     * @param string  $mime_type     The image mime type to get missing sizes for.
    143169     */
    144     return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id );
     170    return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id, $mime_type );
    145171}
    146172
     
    150176 *
    151177 * @since 5.3.0
     178 * @since 6.1.0 Now supports additional mime types, creating the additional sub-sizes and 'full' sized images.
    152179 *
    153180 * @param int $attachment_id The image attachment post ID.
     
    168195        }
    169196    } else {
    170         $missing_sizes = wp_get_missing_image_subsizes( $attachment_id );
    171 
    172         if ( empty( $missing_sizes ) ) {
    173             return $image_meta;
    174         }
    175 
    176         // This also updates the image meta.
    177         $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id );
     197        // Get the primary and additional mime types to generate.
     198        list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $image_file, $attachment_id );
     199
     200        // Generate missing 'full' image files for additional mime types.
     201        if ( ! empty( $additional_mime_types ) ) {
     202            if ( isset( $image_meta['sources'] ) ) {
     203                $missing_mime_types = array_diff( $additional_mime_types, array_keys( $image_meta['sources'] ) );
     204            } else {
     205                $missing_mime_types = $additional_mime_types;
     206            }
     207            if ( ! empty( $missing_mime_types ) ) {
     208                $image_meta = _wp_make_additional_mime_types( $missing_mime_types, $image_file, $image_meta, $attachment_id );
     209            }
     210        }
     211
     212        // Generate missing image sub-sizes for each mime type.
     213        $all_mime_types = array_merge( array( $primary_mime_type ), $additional_mime_types );
     214        foreach ( $all_mime_types as $mime_type ) {
     215            $missing_sizes = wp_get_missing_image_subsizes( $attachment_id, $mime_type );
     216
     217            if ( empty( $missing_sizes ) ) {
     218                continue;
     219            }
     220
     221            // This also updates the image meta.
     222            $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id, $mime_type );
     223        }
    178224    }
    179225
     
    223269
    224270/**
    225  * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata.
     271 * Creates image mime variations and sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata.
    226272 *
    227273 * Intended for use after an image is uploaded. Saves/updates the image metadata after each
     
    229275 *
    230276 * @since 5.3.0
     277 * @since 6.1.0 Generates sub-sizes in alternate mime types based on the `wp_image_mime_transforms` filter.
    231278 *
    232279 * @param string $file          Full path to the image file.
     
    249296        'filesize' => wp_filesize( $file ),
    250297        'sizes'    => array(),
     298        'sources'  => array(),
    251299    );
    252300
     
    258306    }
    259307
     308    // Get the primary and additional mime types to generate.
     309    list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $attachment_id );
     310
     311    list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $primary_mime_type );
     312    if ( is_wp_error( $editor ) ) {
     313        return $image_meta;
     314    }
     315    $suffix = _wp_get_image_suffix( $resized, $rotated );
     316
     317    // Save image only if either it was modified or if the primary mime type is different from the original.
     318    if ( ! empty( $suffix ) || $primary_mime_type !== $imagesize['mime'] ) {
     319        $saved = $editor->save( $editor->generate_filename( $suffix ) );
     320
     321        if ( ! is_wp_error( $saved ) ) {
     322            $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
     323
     324            // If the image was rotated update the stored EXIF data.
     325            if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) {
     326                $image_meta['image_meta']['orientation'] = 1;
     327            }
     328        } else {
     329            // TODO: Log errors.
     330        }
     331    }
     332
     333    // Set 'sources' for the primary mime type.
     334    $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $image_meta );
     335
     336    /*
     337     * Initial save of the new metadata.
     338     * At this point the file was uploaded and moved to the uploads directory
     339     * but the image sub-sizes haven't been created yet and the `sizes` array is empty.
     340     */
     341    wp_update_attachment_metadata( $attachment_id, $image_meta );
     342
     343    if ( ! empty( $additional_mime_types ) ) {
     344        // Use the original file's exif_meta orientation information for secondary mime generation.
     345        $saved_orientation                       = $image_meta['image_meta']['orientation'];
     346        $image_meta['image_meta']['orientation'] = $exif_meta['orientation'];
     347        $image_meta                              = _wp_make_additional_mime_types( $additional_mime_types, $file, $image_meta, $attachment_id );
     348        $image_meta['image_meta']['orientation'] = $saved_orientation;
     349
     350    }
     351
     352    $new_sizes = wp_get_registered_image_subsizes();
     353
     354    /**
     355     * Filters the image sizes automatically generated when uploading an image.
     356     *
     357     * @since 2.9.0
     358     * @since 4.4.0 Added the `$image_meta` argument.
     359     * @since 5.3.0 Added the `$attachment_id` argument.
     360     *
     361     * @param array $new_sizes     Associative array of image sizes to be created.
     362     * @param array $image_meta    The image meta data: width, height, file, sizes, etc.
     363     * @param int   $attachment_id The attachment post ID for the image.
     364     */
     365    $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id );
     366
     367    $image_meta = _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $primary_mime_type );
     368
     369    // Filter secondary mime types to those sizes that are enabled.
     370    $new_sizes = _wp_filter_image_sizes_additional_mime_type_support( $new_sizes, $attachment_id );
     371
     372    foreach ( $additional_mime_types as $additional_mime_type ) {
     373        $image_meta = _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_type );
     374    }
     375
     376    return $image_meta;
     377}
     378
     379/**
     380 * Returns a WP_Image_Editor instance where the image file has been scaled and rotated as necessary.
     381 *
     382 * @since 6.1.0
     383 * @access private
     384 *
     385 * @param string     $file          Full path to the image file.
     386 * @param int        $attachment_id Attachment ID.
     387 * @param array      $imagesize     {
     388 *     Indexed array of the image width and height in pixels.
     389 *
     390 *     @type int $0 The image width.
     391 *     @type int $1 The image height.
     392 * }
     393 * @param array|null $exif_meta EXIF metadata if extracted from the image file.
     394 * @param string     $mime_type Output mime type.
     395 * @return array Array with three entries: The WP_Image_Editor instance, whether the image was resized, and whether the
     396 *               image was rotated (booleans). Each entry can alternatively be a WP_Error in case something went wrong.
     397 */
     398function _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type ) {
     399    $resized = false;
     400    $rotated = false;
     401
     402    $editor = wp_get_image_editor( $file, array( 'mime_type' => $mime_type ) );
     403    if ( is_wp_error( $editor ) ) {
     404        // This image cannot be edited.
     405        return array( $editor, $resized, $rotated );
     406    }
     407
     408    if ( ! empty( $mime_type ) ) {
     409        $editor->set_output_mime_type( $mime_type );
     410    }
     411
    260412    // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736.
    261     if ( 'image/png' !== $imagesize['mime'] ) {
    262 
     413    if ( 'image/png' !== $mime_type ) {
    263414        /**
    264415         * Filters the "BIG image" threshold value.
     
    286437        // If the original image's dimensions are over the threshold,
    287438        // scale the image and use it as the "full" size.
    288         if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
    289             $editor = wp_get_image_editor( $file );
    290 
    291             if ( is_wp_error( $editor ) ) {
    292                 // This image cannot be edited.
    293                 return $image_meta;
    294             }
    295 
     439        if ( $threshold && ( $imagesize[0] > $threshold || $imagesize[1] > $threshold ) ) {
    296440            // Resize the image.
    297441            $resized = $editor->resize( $threshold, $threshold );
    298             $rotated = null;
    299442
    300443            // If there is EXIF data, rotate according to EXIF Orientation.
    301444            if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) {
    302                 $resized = $editor->maybe_exif_rotate();
    303                 $rotated = $resized;
    304             }
    305 
    306             if ( ! is_wp_error( $resized ) ) {
    307                 // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
    308                 // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
    309                 $saved = $editor->save( $editor->generate_filename( 'scaled' ) );
    310 
    311                 if ( ! is_wp_error( $saved ) ) {
    312                     $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
    313 
    314                     // If the image was rotated update the stored EXIF data.
    315                     if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) {
    316                         $image_meta['image_meta']['orientation'] = 1;
    317                     }
    318                 } else {
    319                     // TODO: Log errors.
    320                 }
    321             } else {
    322                 // TODO: Log errors.
     445                $rotated = $editor->maybe_exif_rotate();
    323446            }
    324447        } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) {
    325448            // Rotate the whole original image if there is EXIF data and "orientation" is not 1.
    326 
    327             $editor = wp_get_image_editor( $file );
    328 
    329             if ( is_wp_error( $editor ) ) {
    330                 // This image cannot be edited.
    331                 return $image_meta;
    332             }
    333 
    334             // Rotate the image.
    335449            $rotated = $editor->maybe_exif_rotate();
    336 
    337             if ( true === $rotated ) {
    338                 // Append `-rotated` to the image file name.
    339                 $saved = $editor->save( $editor->generate_filename( 'rotated' ) );
    340 
    341                 if ( ! is_wp_error( $saved ) ) {
    342                     $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
    343 
    344                     // Update the stored EXIF data.
    345                     if ( ! empty( $image_meta['image_meta']['orientation'] ) ) {
    346                         $image_meta['image_meta']['orientation'] = 1;
    347                     }
    348                 } else {
    349                     // TODO: Log errors.
    350                 }
    351             }
    352         }
    353     }
    354 
    355     /*
    356      * Initial save of the new metadata.
    357      * At this point the file was uploaded and moved to the uploads directory
    358      * but the image sub-sizes haven't been created yet and the `sizes` array is empty.
    359      */
    360     wp_update_attachment_metadata( $attachment_id, $image_meta );
    361 
    362     $new_sizes = wp_get_registered_image_subsizes();
    363 
    364     /**
    365      * Filters the image sizes automatically generated when uploading an image.
    366      *
    367      * @since 2.9.0
    368      * @since 4.4.0 Added the `$image_meta` argument.
    369      * @since 5.3.0 Added the `$attachment_id` argument.
    370      *
    371      * @param array $new_sizes     Associative array of image sizes to be created.
    372      * @param array $image_meta    The image meta data: width, height, file, sizes, etc.
    373      * @param int   $attachment_id The attachment post ID for the image.
    374      */
    375     $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id );
    376 
    377     return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id );
     450        }
     451    }
     452
     453    return array( $editor, $resized, $rotated );
     454}
     455
     456/**
     457 * Gets the suffix to use for image files based on resizing and rotating.
     458 *
     459 * @since 6.1.0
     460 * @access private
     461 *
     462 * @param bool|WP_Error Whether the image was resized, or an error if resizing failed.
     463 * @param bool|WP_Error Whether the image was rotated, or an error if rotating failed.
     464 * @return string The suffix to use for the file name, or empty string if none.
     465 */
     466function _wp_get_image_suffix( $resized, $rotated ) {
     467    if ( $resized && ! is_wp_error( $resized ) ) {
     468        // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
     469        // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
     470        return 'scaled';
     471    }
     472
     473    if ( true === $rotated ) {
     474        // Append `-rotated` to the image file name.
     475        return 'rotated';
     476    }
     477
     478    if ( is_wp_error( $resized ) || is_wp_error( $rotated ) ) {
     479        // TODO: Log errors.
     480    }
     481    return '';
     482}
     483
     484/**
     485 * Gets a sources array element from a meta.
     486 *
     487 * @since 6.1.0
     488 * @access private
     489 *
     490 * @param array $meta The meta to get the source from.
     491 * @return array The source array element.
     492 */
     493function _wp_get_sources_from_meta( $meta ) {
     494    return array(
     495        'file'     => isset( $meta['file'] ) ? wp_basename( $meta['file'] ) : '',
     496        'filesize' => isset( $meta['filesize'] ) ? $meta['filesize'] : wp_filesize( $meta['path'] ),
     497    );
    378498}
    379499
     
    385505 *
    386506 * @since 5.3.0
     507 * @since 6.1.0 The $mime_type parameter was added.
    387508 * @access private
    388509 *
    389  * @param array  $new_sizes     Array defining what sizes to create.
    390  * @param string $file          Full path to the image file.
    391  * @param array  $image_meta    The attachment meta data array.
    392  * @param int    $attachment_id Attachment ID to process.
     510 * @param array  $new_sizes       Array defining what sizes to create.
     511 * @param string $file            Full path to the image file.
     512 * @param array  $image_meta      The attachment meta data array.
     513 * @param int    $attachment_id   Attachment ID to process.
     514 * @param string $mime_type       Optional. The mime type to check for missing sizes. Default is the image mime of $file.
    393515 * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing.
    394516 */
    395 function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) {
     517function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mime_type = '' ) {
    396518    if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
    397519        // Not an image attachment.
    398520        return array();
     521    }
     522
     523    if ( ! $mime_type ) {
     524        $mime_type = wp_get_image_mime( $file );
    399525    }
    400526
     
    408534             */
    409535            if ( array_key_exists( $size_name, $new_sizes ) ) {
    410                 unset( $new_sizes[ $size_name ] );
     536                // Unset the size if it is either the required mime type already exists either as main mime type or
     537                // within sources.
     538                if ( $size_meta['mime-type'] === $mime_type || isset( $size_meta['sources'][ $mime_type ] ) ) {
     539                    unset( $new_sizes[ $size_name ] );
     540                }
    411541            }
    412542        }
     
    434564    $new_sizes = array_filter( array_merge( $priority, $new_sizes ) );
    435565
    436     $editor = wp_get_image_editor( $file );
     566    $editor = wp_get_image_editor( $file, array( 'mime_type' => $mime_type ) );
    437567
    438568    if ( is_wp_error( $editor ) ) {
     
    440570        return $image_meta;
    441571    }
     572
     573    $editor->set_output_mime_type( $mime_type );
    442574
    443575    // If stored EXIF data exists, rotate the source image before creating sub-sizes.
     
    458590            } else {
    459591                // Save the size meta value.
    460                 $image_meta['sizes'][ $new_size_name ] = $new_size_meta;
     592                if ( ! isset( $image_meta['sizes'][ $new_size_name ] ) ) {
     593                    $image_meta['sizes'][ $new_size_name ] = $new_size_meta;
     594                } else {
     595                    // Remove any newly generated images that are larger than the primary mime type.
     596                    $new_size     = isset( $new_size_meta['filesize'] ) ? $new_size_meta['filesize'] : 0;
     597                    $primary_size = isset( $image_meta['sizes'][ $new_size_name ]['filesize'] ) ? $image_meta['sizes'][ $new_size_name ]['filesize'] : 0;
     598
     599                    if ( $new_size && $primary_size && $new_size >= $primary_size ) {
     600                        wp_delete_file( dirname( $file ) . '/' . $new_size_meta['file'] );
     601                        continue;
     602                    }
     603                }
     604                if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) {
     605                    $image_meta['sizes'][ $new_size_name ]['sources'] = array();
     606                }
     607                $image_meta['sizes'][ $new_size_name ]['sources'][ $mime_type ] = _wp_get_sources_from_meta( $new_size_meta );
    461608                wp_update_attachment_metadata( $attachment_id, $image_meta );
    462609            }
     
    467614
    468615        if ( ! empty( $created_sizes ) ) {
    469             $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes );
     616            foreach ( $created_sizes as $created_size_name => $created_size_meta ) {
     617
     618                // Primary mime type is set in 'sizes' array.
     619                if ( ! isset( $image_meta['sizes'][ $created_size_name ] ) ) {
     620                    $image_meta['sizes'][ $created_size_name ] = $created_size_meta;
     621                } else {
     622                    // Remove any newly generated images that are larger than the primary mime type.
     623                    $new_size     = isset( $created_size_meta['filesize'] ) ? $created_size_meta['filesize'] : 0;
     624                    $primary_size = isset( $image_meta['sizes'][ $created_size_name ]['filesize'] ) ? $image_meta['sizes'][ $created_size_name ]['filesize'] : 0;
     625
     626                    if ( $new_size && $primary_size && $new_size >= $primary_size ) {
     627                        wp_delete_file( dirname( $file ) . '/' . $created_size_meta['file'] );
     628                        continue;
     629                    }
     630                }
     631                if ( ! isset( $image_meta['sizes'][ $created_size_name ]['sources'] ) ) {
     632                    $image_meta['sizes'][ $created_size_name ]['sources'] = array();
     633                }
     634                $image_meta['sizes'][ $created_size_name ]['sources'][ $mime_type ] = _wp_get_sources_from_meta( $new_size_meta );
     635            }
    470636            wp_update_attachment_metadata( $attachment_id, $image_meta );
    471637        }
     
    473639
    474640    return $image_meta;
     641}
     642
     643/**
     644 * Filters the list of image size objects that support secondary mime type output.
     645 *
     646 * @since 6.1.0
     647 *
     648 * @param array $sizes         Associative array of image sizes.
     649 * @param int   $attachment_id Attachment ID.
     650 * @return array $sizes Filtered $sizes with only those that support secondary mime type output.
     651 */
     652function _wp_filter_image_sizes_additional_mime_type_support( $sizes, $attachment_id ) {
     653
     654    // Include only the core sizes that do not rely on add_image_size(). Additional image sizes are opt-in.
     655    $enabled_sizes = array(
     656        'thumbnail'      => true,
     657        'medium'         => true,
     658        'medium_large'   => true,
     659        'large'          => true,
     660        'post-thumbnail' => true,
     661    );
     662
     663    /**
     664     * Filter the sizes that support secondary mime type output. Developers can use this
     665     * to control the output of additional mime type sub-sized images.
     666     *
     667     * @since 6.1.0
     668     *
     669     * @param array $enabled_sizes Map of size names and whether they support secondary mime type output.
     670     * @param int   $attachment_id Attachment ID.
     671     */
     672    $enabled_sizes = apply_filters( 'wp_image_sizes_with_additional_mime_type_support', $enabled_sizes, $attachment_id );
     673
     674    // Filter supported sizes to only include enabled sizes.
     675    return array_intersect_key( $sizes, array_filter( $enabled_sizes ) );
     676}
     677
     678/**
     679 * Low-level function to create full-size images in additional mime types.
     680 *
     681 * Updates the image meta after each mime type image is created.
     682 *
     683 * @since 6.1.0
     684 * @access private
     685 *
     686 * @param array  $new_mime_types Array defining what mime types to create.
     687 * @param string $file           Full path to the image file.
     688 * @param array  $image_meta     The attachment meta data array.
     689 * @param int    $attachment_id  Attachment ID to process.
     690 * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing.
     691 */
     692function _wp_make_additional_mime_types( $new_mime_types, $file, $image_meta, $attachment_id ) {
     693    $imagesize          = array(
     694        $image_meta['width'],
     695        $image_meta['height'],
     696    );
     697    $exif_meta          = isset( $image_meta['image_meta'] ) ? $image_meta['image_meta'] : null;
     698    $original_file_size = isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file );
     699
     700    foreach ( $new_mime_types as $mime_type ) {
     701        list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type );
     702        if ( is_wp_error( $editor ) ) {
     703            // The image cannot be edited.
     704            continue;
     705        }
     706
     707        $suffix    = _wp_get_image_suffix( $resized, $rotated );
     708        $extension = wp_get_default_extension_for_mime_type( $mime_type );
     709
     710        $saved = $editor->save( $editor->generate_filename( $suffix, null, $extension ) );
     711
     712        if ( is_wp_error( $saved ) ) {
     713            // TODO: Log errors.
     714        } else {
     715            // If the saved image is larger than the original, discard it.
     716            $filesize = isset( $saved['filesize'] ) ? $saved['filesize'] : wp_filesize( $saved['path'] );
     717            if ( $filesize && $original_file_size && $filesize > $original_file_size ) {
     718                wp_delete_file( $saved['path'] );
     719                continue;
     720            }
     721            $image_meta['sources'][ $mime_type ] = _wp_get_sources_from_meta( $saved );
     722            wp_update_attachment_metadata( $attachment_id, $image_meta );
     723        }
     724    }
     725
     726    return $image_meta;
     727}
     728
     729
     730/**
     731 * Check if an image belongs to an attachment.
     732 *
     733 * @since 6.1.0
     734 * @access private
     735 *
     736 * @param string $filename     Full path to the image file.
     737 * @param int   $attachment_id Attachment ID to check.
     738 * @return bool True if the image belongs to the attachment, false otherwise.
     739 */
     740function _wp_image_belongs_to_attachment( $filename, $attachment_id ) {
     741    $meta_data = wp_get_attachment_metadata( $attachment_id );
     742
     743    if ( ! isset( $image_meta['sizes'] ) ) {
     744        return false;
     745    }
     746    $sizes = $image_meta['sizes'];
     747    foreach ( $sizes as $size ) {
     748        if ( $size['file'] === $filename ) {
     749            return true;
     750        }
     751        if ( isset( $size['sources'] ) && is_array( $size['sources'] ) ) {
     752            foreach ( $size['sources'] as $source ) {
     753                if ( $source['file'] === $filename ) {
     754                    return true;
     755                }
     756            }
     757        }
     758    }
     759    return false;
    475760}
    476761
     
    631916
    632917                    // Create sub-sizes saving the image meta after each.
    633                     $metadata = _wp_make_subsizes( $merged_sizes, $image_file, $metadata, $attachment_id );
     918                    $metadata = _wp_make_subsizes( $merged_sizes, $image_file, $metadata, $attachment_id, '' );
    634919                }
    635920            }
     
    11581443    return $dst_file;
    11591444}
     1445
     1446/**
     1447 * Returns an array with the list of valid mime types that a specific mime type should be converted into.
     1448 * For example an `image/jpeg` should be converted into an `image/jpeg` and `image/webp`. The first type
     1449 * is considered the primary output type for this image.
     1450 *
     1451 * Called for each uploaded image to determine the list of mime types that should be converted into. Then,
     1452 * called again for each image size as they are generated to check if the image should be converted into the mime type
     1453 * for that size.
     1454 *
     1455 * @since 6.1.0
     1456 *
     1457 * @param int    $attachment_id  The attachment ID.
     1458 * @return array An array of valid mime types, where the key is the source file mime type and the list of mime types to
     1459 *               generate.
     1460 */
     1461function wp_upload_image_mime_transforms( $attachment_id ) {
     1462    $default_image_mime_transforms = array(
     1463        'image/jpeg' => array( 'image/jpeg', 'image/webp' ),
     1464        'image/webp' => array( 'image/webp', 'image/jpeg' ),
     1465    );
     1466    $image_mime_transforms         = $default_image_mime_transforms;
     1467
     1468    /**
     1469     * Filter the output mime types for a given input mime type and image size.
     1470     *
     1471     * @since 6.1.0
     1472     *
     1473     * @param array  $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type
     1474     *                                      and the value is one or more mime file types to generate.
     1475     * @param int    $attachment_id         The ID of the attachment where the hook was dispatched.
     1476     */
     1477    $image_mime_transforms = apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id );
     1478
     1479    if ( ! is_array( $image_mime_transforms ) ) {
     1480        return $default_image_mime_transforms;
     1481    }
     1482
     1483    return array_map(
     1484        function( $transforms_list ) {
     1485            return (array) $transforms_list;
     1486        },
     1487        $image_mime_transforms
     1488    );
     1489}
     1490
     1491/**
     1492 * Extract the primary and additional mime output types for an image from the $image_mime_transforms.
     1493 *
     1494 * @since 6.1.0
     1495 * @access private
     1496 *
     1497 * @param string $file          Full path to the image file.
     1498 * @param int    $attachment_id Attachment ID to process.
     1499 * @return array An array with two entries, the primary mime type and the list of additional mime types.
     1500 */
     1501function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) {
     1502    $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id );
     1503    $original_mime_type    = wp_get_image_mime( $file );
     1504    $output_mime_types     = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type );
     1505
     1506    // Exclude any output mime types that the system doesn't support.
     1507    $output_mime_types = array_values(
     1508        array_filter(
     1509            $output_mime_types,
     1510            function( $mime_type ) {
     1511                return wp_image_editor_supports(
     1512                    array(
     1513                        'mime_type' => $mime_type,
     1514                    )
     1515                );
     1516            }
     1517        )
     1518    );
     1519
     1520    // Handle an empty value for $output_mime_types: only output the original type.
     1521    if ( empty( $output_mime_types ) ) {
     1522        return array( $original_mime_type, array() );
     1523    }
     1524
     1525    // Use original mime type as primary mime type, or alternatively the first one.
     1526    $primary_mime_type_key = array_search( $original_mime_type, $output_mime_types, true );
     1527    if ( false === $primary_mime_type_key ) {
     1528        $primary_mime_type_key = 0;
     1529    }
     1530    // Split output mime types into primary mime type and additional mime types.
     1531    $additional_mime_types     = $output_mime_types;
     1532    list( $primary_mime_type ) = array_splice( $additional_mime_types, $primary_mime_type_key, 1 );
     1533
     1534    return array(
     1535        $primary_mime_type,
     1536        $additional_mime_types,
     1537    );
     1538}
  • trunk/src/wp-includes/class-wp-image-editor.php

    r53547 r53751  
    334334    protected function get_output_format( $filename = null, $mime_type = null ) {
    335335        $new_ext = null;
     336
     337        // If no mime type is passed but output mime type is set, use that.
     338        if ( ! $mime_type && ! empty( $this->output_mime_type ) ) {
     339            $mime_type = $this->output_mime_type;
     340        }
    336341
    337342        // By default, assume specified type takes priority.
     
    426431
    427432    /**
    428      * Builds an output filename based on current file, and adding proper suffix
    429      *
    430      * @since 3.5.0
    431      *
    432      * @param string $suffix
    433      * @param string $dest_path
    434      * @param string $extension
    435      * @return string filename
     433     * Builds an output filename based on current file, and adding proper suffix.
     434     *
     435     * @since 3.5.0
     436     * @since 6.1.0 Skips adding a suffix when set to an empty string. When the
     437     *              file extension being generated doesn't match the image file extension,
     438     *              add the extension to the suffix
     439     *
     440     * @param string $suffix    Optional. Suffix to add to the filename. The default null
     441     *                          will result in a 'widthxheight' suffix. Passing
     442     *                          an empty string will result in no suffix.
     443     * @param string $dest_path Optional. The path to save the file to. The default null
     444     *                          will use the image file path.
     445     * @param string $extension Optional. The file extension to use. The default null
     446     *                          will use the image file extension.
     447     * @return string filename The generated file name.
    436448     */
    437449    public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
    438450        // $suffix will be appended to the destination filename, just before the extension.
    439         if ( ! $suffix ) {
     451        if ( null === $suffix ) {
    440452            $suffix = $this->get_suffix();
    441453        }
     
    458470        }
    459471
    460         return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
     472        if ( empty( $suffix ) ) {
     473            $suffix = '';
     474        } else {
     475            $suffix = "-{$suffix}";
     476        }
     477
     478        // When the file extension being generated doesn't match the image file extension,
     479        // add the extension to the suffix to ensure a unique file name. Prevents
     480        // name conflicts when a single image type can have multiple extensions,
     481        // eg. .jpg, .jpeg and .jpe are all valid JPEG extensions.
     482        if ( ! empty( $extension ) && $extension !== $ext ) {
     483            $suffix .= "-{$ext}";
     484        }
     485
     486        return trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}";
    461487    }
    462488
     
    638664        return wp_get_default_extension_for_mime_type( $mime_type );
    639665    }
     666
     667    /**
     668     * Set the editor output mime type, useful when outputting alternate mime types.
     669     *
     670     * Track that the mime type is set with the mime type set flag.
     671     *
     672     * @since 6.1.0
     673     *
     674     * @param string $output_mime_type The mime type to set.
     675     */
     676    public function set_output_mime_type( $output_mime_type ) {
     677        $this->output_mime_type = $output_mime_type;
     678    }
     679
     680    /**
     681     * Reset the mime type to the original file mime type.
     682     *
     683     * Reset the mime type set flag.
     684     *
     685     * @since 6.1.0
     686     */
     687    public function reset_output_mime_type() {
     688        $this->output_mime_type = $this->mime_type;
     689    }
    640690}
    641 
  • trunk/src/wp-includes/media.php

    r53715 r53751  
    18531853            }
    18541854
     1855            // Use alternate mime types when specified and available.
     1856            if ( $attachment_id > 0 && _wp_in_front_end_context() ) {
     1857                $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $context, $attachment_id );
     1858            }
     1859
    18551860            /**
    18561861             * Filters an img tag within the content for a given context.
     
    18971902
    18981903    return $content;
     1904}
     1905
     1906/**
     1907 * Use alternate mime type images in the front end content output when available.
     1908 *
     1909 * @since 6.1.0
     1910 *
     1911 * @param string $image         The HTML `img` tag where the attribute should be added.
     1912 * @param string $context       Additional context to pass to the filters.
     1913 * @param int    $attachment_id The attachment ID.
     1914 * @return string Converted `img` tag with `loading` attribute added.
     1915 */
     1916function wp_image_use_alternate_mime_types( $image, $context, $attachment_id ) {
     1917    $metadata = wp_get_attachment_metadata( $attachment_id );
     1918    if ( empty( $metadata['file'] ) ) {
     1919        return $image;
     1920    }
     1921
     1922    // Only alter images with a `sources` attribute
     1923    if ( empty( $metadata['sources'] ) ) {
     1924        return $image;
     1925    };
     1926
     1927    $target_mimes = array( 'image/webp', 'image/jpeg' );
     1928
     1929    /**
     1930     * Filter the content image mime type output selection and order.
     1931     *
     1932     * When outputting images in the content, the first mime type available will be used.
     1933     *
     1934     * @since 6.1.0
     1935     *
     1936     * @param array  $target_mimes  The image output mime type and order. Default is array( 'image/webp', 'image/jpeg' ).
     1937     * @param int    $attachment_id The attachment ID.
     1938     * @param string $context       Additional context to pass to the filters.
     1939     * @return array The filtered output mime type and order. Return an empty array to skip mime type substitution.
     1940     */
     1941    $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes, $attachment_id, $context );
     1942
     1943    if ( false === $target_mimes ) {
     1944        return $image;
     1945    }
     1946
     1947    // Find the appropriate size for the provided URL in the first available mime type.
     1948    foreach ( $target_mimes as $target_mime ) {
     1949        // Handle full size image replacement.
     1950        if ( ! empty( $metadata['sources'][ $target_mime ]['file'] ) ) {
     1951            $src_filename = wp_basename( $metadata['file'] );
     1952
     1953            // This is the same MIME type as the original, so the entire $target_mime can be skipped.
     1954            // Since it is already the preferred MIME type, the entire loop can be cancelled.
     1955            if ( $metadata['sources'][ $target_mime ]['file'] === $src_filename ) {
     1956                break;
     1957            }
     1958
     1959            $image = str_replace( $src_filename, $metadata['sources'][ $target_mime ]['file'], $image );
     1960
     1961            // The full size was replaced, so unset this entirely here so that in the next iteration it is no longer
     1962            // considered, simply for a small performance optimization.
     1963            unset( $metadata['sources'] );
     1964        }
     1965
     1966        // Go through each image size and replace with the first available mime type version.
     1967        foreach ( $metadata['sizes'] as $name => $size_data ) {
     1968            // Check if size has an original file.
     1969            if ( empty( $size_data['file'] ) ) {
     1970                continue;
     1971            }
     1972
     1973            // Check if size has a source in the desired mime type.
     1974            if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) {
     1975                continue;
     1976            }
     1977
     1978            $src_filename = wp_basename( $size_data['file'] );
     1979
     1980            // This is the same MIME type as the original, so the entire $target_mime can be skipped.
     1981            // Since it is already the preferred MIME type, the entire loop can be cancelled.
     1982            if ( $size_data['sources'][ $target_mime ]['file'] === $src_filename ) {
     1983                break 2;
     1984            }
     1985
     1986            // Found a match, replace with the new filename.
     1987            $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image );
     1988
     1989            // This size was replaced, so unset this entirely here so that in the next iteration it is no longer
     1990            // considered, simply for a small performance optimization.
     1991            unset( $metadata['sizes'][ $name ] );
     1992        }
     1993    }
     1994    return $image;
     1995}
     1996
     1997/**
     1998 * Check if execution is currently in the front end content context, outside of <head>.
     1999 *
     2000 * @since 6.1.0
     2001 * @access private
     2002 *
     2003 * @return bool True if in the front end content context, false otherwise.
     2004 */
     2005function _wp_in_front_end_context() {
     2006    global $wp_query;
     2007
     2008    // Check if this request is generally outside (or before) any frontend context.
     2009    if ( ! isset( $wp_query ) || defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || is_feed() ) {
     2010        return false;
     2011    }
     2012
     2013    // Check if we're anywhere before the 'wp_head' action has completed.
     2014    return did_action( 'template_redirect' ) && ! doing_action( 'wp_head' );
    18992015}
    19002016
  • trunk/src/wp-includes/post.php

    r53723 r53751  
    64826482
    64836483        foreach ( $meta['sizes'] as $size => $sizeinfo ) {
    6484             $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file );
    6485 
    6486             if ( ! empty( $intermediate_file ) ) {
    6487                 $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file );
    6488 
    6489                 if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) {
    6490                     $deleted = false;
     6484
     6485            // Check for alternate size mime types in the sizeinfo['sources'] array to delete.
     6486            if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) {
     6487                foreach ( $sizeinfo['sources'] as $mime => $properties ) {
     6488                    $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file );
     6489                    if ( ! empty( $intermediate_file ) ) {
     6490                        $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file );
     6491                        if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) {
     6492                            $deleted = false;
     6493                        }
     6494                    }
     6495                }
     6496            } else {
     6497                // Otherwise, delete files from the sizeinfo data.
     6498                $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file );
     6499
     6500                if ( ! empty( $intermediate_file ) ) {
     6501                    $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file );
     6502
     6503                    if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) {
     6504                        $deleted = false;
     6505                    }
    64916506                }
    64926507            }
     
    65106525    }
    65116526
     6527    // Delete the full size images from 'sources' if available, or the root file.
     6528    if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) {
     6529        $sources          = $meta['sources'];
     6530        $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) );
     6531        foreach ( $sources as $mime => $properties ) {
     6532            if ( ! is_array( $properties ) || empty( $properties['file'] ) ) {
     6533                continue;
     6534            }
     6535            $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file );
     6536            if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) {
     6537                $deleted = false;
     6538            }
     6539        }
     6540    } else {
     6541        if ( ! wp_delete_file_from_directory( $file, $uploadpath['basedir'] ) ) {
     6542            $deleted = false;
     6543        }
     6544    }
     6545
    65126546    if ( is_array( $backup_sizes ) ) {
     6547
    65136548        $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) );
    6514 
     6549        // Delete the root (edited) file which was not deleted above.
     6550        if ( ! wp_delete_file_from_directory( $file, $uploadpath['basedir'] ) ) {
     6551            $deleted = false;
     6552        }
    65156553        foreach ( $backup_sizes as $size ) {
    6516             $del_file = path_join( dirname( $meta['file'] ), $size['file'] );
    6517 
    6518             if ( ! empty( $del_file ) ) {
    6519                 $del_file = path_join( $uploadpath['basedir'], $del_file );
    6520 
    6521                 if ( ! wp_delete_file_from_directory( $del_file, $del_dir ) ) {
    6522                     $deleted = false;
     6554            // Delete files from 'sources' data if available, otherwise from 'sizes' data.
     6555            if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) {
     6556                // Delete any backup images stored in the 'sources' array.
     6557                if ( isset( $size['sources'] ) && is_array( $size['sources'] ) ) {
     6558                    foreach ( $size['sources'] as $mime => $properties ) {
     6559                        $del_file = path_join( dirname( $meta['file'] ), $properties['file'] );
     6560                        if ( ! empty( $del_file ) ) {
     6561                            $del_file = path_join( $uploadpath['basedir'], $del_file );
     6562                            if ( ! wp_delete_file_from_directory( $del_file, $del_dir ) ) {
     6563                                $deleted = false;
     6564                            }
     6565                        }
     6566                    }
     6567                }
     6568            } else {
     6569                $del_file = path_join( dirname( $meta['file'] ), $size['file'] );
     6570
     6571                if ( ! empty( $del_file ) ) {
     6572                    $del_file = path_join( $uploadpath['basedir'], $del_file );
     6573                    if ( ! wp_delete_file_from_directory( $del_file, $del_dir ) ) {
     6574                        $deleted = false;
     6575                    }
    65236576                }
    65246577            }
    65256578        }
    6526     }
    6527 
    6528     if ( ! wp_delete_file_from_directory( $file, $uploadpath['basedir'] ) ) {
    6529         $deleted = false;
    65306579    }
    65316580
  • trunk/tests/phpunit/tests/image/editor.php

    r52248 r53751  
    132132
    133133        // Removing PNG to WEBP conversion on save. Quality setting should reset to the default.
     134        $editor->reset_output_mime_type();
    134135        remove_filter( 'image_editor_output_format', array( $this, 'image_editor_output_formats' ) );
    135136        $editor->save();
     
    155156
    156157        // After removing the conversion the quality setting should reset to the filtered value for the original image type, JPEG.
     158        $editor->reset_output_mime_type();
    157159        remove_filter( 'image_editor_output_format', array( $this, 'image_editor_output_formats' ) );
    158160        $editor->save();
     
    227229
    228230        // Test with a suffix only.
    229         $this->assertSame( 'canola-100x50.png', wp_basename( $editor->generate_filename( null, null, 'png' ) ) );
     231        $this->assertSame( 'canola-100x50-jpg.png', wp_basename( $editor->generate_filename( null, null, 'png' ) ) );
    230232
    231233        // Combo!
    232         $this->assertSame( trailingslashit( realpath( get_temp_dir() ) ) . 'canola-new.png', $editor->generate_filename( 'new', realpath( get_temp_dir() ), 'png' ) );
     234        $this->assertSame( trailingslashit( realpath( get_temp_dir() ) ) . 'canola-new-jpg.png', $editor->generate_filename( 'new', realpath( get_temp_dir() ), 'png' ) );
    233235
    234236        // Test with a stream destination.
     
    363365    }
    364366
     367    /**
     368     * Test creating  the original image mime type when the image is uploaded.
     369     *
     370     * @ticket 55443
     371     *
     372     * @dataProvider provider_image_with_default_behaviors_during_upload
     373     */
     374    public function it_should_create_the_original_image_mime_type_when_the_image_is_uploaded( $file_location, $expected_mime, $targeted_mime ) {
     375        $attachment_id = $this->factory->attachment->create_upload_object( $file_location );
     376
     377        $metadata = wp_get_attachment_metadata( $attachment_id );
     378
     379        $this->assertIsArray( $metadata );
     380        foreach ( $metadata['sizes'] as $size_name => $properties ) {
     381            $this->assertArrayHasKey( 'sources', $properties );
     382            $this->assertIsArray( $properties['sources'] );
     383            $this->assertArrayHasKey( $expected_mime, $properties['sources'] );
     384            $this->assertArrayHasKey( 'filesize', $properties['sources'][ $expected_mime ] );
     385            $this->assertArrayHasKey( 'file', $properties['sources'][ $expected_mime ] );
     386            $this->assertArrayHasKey( $targeted_mime, $properties['sources'] );
     387            $this->assertArrayHasKey( 'filesize', $properties['sources'][ $targeted_mime ] );
     388            $this->assertArrayHasKey( 'file', $properties['sources'][ $targeted_mime ] );
     389        }
     390    }
     391
     392    /**
     393     * Data provider for it_should_create_the_original_image_mime_type_when_the_image_is_uploaded.
     394     */
     395    public function provider_image_with_default_behaviors_during_upload() {
     396        yield 'JPEG image' => array(
     397            DIR_TESTDATA . '/images/test-image.jpg',
     398            'image/jpeg',
     399            'image/webp',
     400        );
     401
     402        yield 'WebP image' => array(
     403            DIR_TESTDATA . '/images/webp-lossy.webp',
     404            'image/webp',
     405            'image/jpeg',
     406        );
     407    }
     408
     409    /**
     410     * Test Do not create the sources property if no transform is provided.
     411     *
     412     * @ticket 55443
     413     */
     414    public function it_should_not_create_the_sources_property_if_no_transform_is_provided() {
     415        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     416
     417        $attachment_id = $this->factory->attachment->create_upload_object(
     418            DIR_TESTDATA . '/images/test-image.jpg'
     419        );
     420
     421        $metadata = wp_get_attachment_metadata( $attachment_id );
     422
     423        $this->assertIsArray( $metadata );
     424        foreach ( $metadata['sizes'] as $size_name => $properties ) {
     425            $this->assertArrayNotHasKey( 'sources', $properties );
     426        }
     427    }
     428
     429    /**
     430     * Test creating the sources property when no transform is available.
     431     *
     432     * @ticket 55443
     433     */
     434    public function it_should_create_the_sources_property_when_no_transform_is_available() {
     435        add_filter(
     436            'wp_upload_image_mime_transforms',
     437            function () {
     438                return array( 'image/jpeg' => array() );
     439            }
     440        );
     441
     442        $attachment_id = $this->factory->attachment->create_upload_object(
     443            DIR_TESTDATA . '/images/test-image.jpg'
     444        );
     445
     446        $metadata = wp_get_attachment_metadata( $attachment_id );
     447
     448        $this->assertIsArray( $metadata );
     449        foreach ( $metadata['sizes'] as $size_name => $properties ) {
     450            $this->assertArrayHasKey( 'sources', $properties );
     451            $this->assertIsArray( $properties['sources'] );
     452            $this->assertArrayHasKey( 'image/jpeg', $properties['sources'] );
     453            $this->assertArrayHasKey( 'filesize', $properties['sources']['image/jpeg'] );
     454            $this->assertArrayHasKey( 'file', $properties['sources']['image/jpeg'] );
     455            $this->assertArrayNotHasKey( 'image/webp', $properties['sources'] );
     456        }
     457    }
     458
     459    /**
     460     * Test not creating the sources property if the mime is not specified on the transforms images.
     461     *
     462     * @ticket 55443
     463     */
     464    public function it_should_not_create_the_sources_property_if_the_mime_is_not_specified_on_the_transforms_images() {
     465        add_filter(
     466            'wp_upload_image_mime_transforms',
     467            function () {
     468                return array( 'image/jpeg' => array() );
     469            }
     470        );
     471
     472        $attachment_id = $this->factory->attachment->create_upload_object(
     473            DIR_TESTDATA . '/images/webp-lossy.webp'
     474        );
     475
     476        $metadata = wp_get_attachment_metadata( $attachment_id );
     477
     478        $this->assertIsArray( $metadata );
     479        foreach ( $metadata['sizes'] as $size_name => $properties ) {
     480            $this->assertArrayNotHasKey( 'sources', $properties );
     481        }
     482    }
     483
     484
     485    /**
     486     * Test creating a WebP version with all the required properties.
     487     *
     488     * @ticket 55443
     489     */
     490    public function it_should_create_a_webp_version_with_all_the_required_properties() {
     491        $attachment_id = $this->factory->attachment->create_upload_object(
     492            DIR_TESTDATA . '/images/test-image.jpg'
     493        );
     494
     495        $metadata = wp_get_attachment_metadata( $attachment_id );
     496        $this->assertArrayHasKey( 'sources', $metadata['sizes']['thumbnail'] );
     497        $this->assertArrayHasKey( 'image/jpeg', $metadata['sizes']['thumbnail']['sources'] );
     498        $this->assertArrayHasKey( 'filesize', $metadata['sizes']['thumbnail']['sources']['image/jpeg'] );
     499        $this->assertArrayHasKey( 'file', $metadata['sizes']['thumbnail']['sources']['image/jpeg'] );
     500        $this->assertArrayHasKey( 'image/webp', $metadata['sizes']['thumbnail']['sources'] );
     501        $this->assertArrayHasKey( 'filesize', $metadata['sizes']['thumbnail']['sources']['image/webp'] );
     502        $this->assertArrayHasKey( 'file', $metadata['sizes']['thumbnail']['sources']['image/webp'] );
     503        $this->assertStringEndsNotWith( '.jpeg', $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] );
     504        $this->assertStringEndsWith( '.webp', $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] );
     505    }
     506
     507    /**
     508     * Test removing `scaled` suffix from the generated filename.
     509     *
     510     * @ticket 55443
     511     */
     512    public function it_should_remove_scaled_suffix_from_the_generated_filename() {
     513        // The leafs image is 1080 pixels wide with this filter we ensure a -scaled version is created.
     514        add_filter(
     515            'big_image_size_threshold',
     516            function () {
     517                return 850;
     518            }
     519        );
     520
     521        $attachment_id = $this->factory->attachment->create_upload_object(
     522            DIR_TESTDATA . '/images/test-image.jpg'
     523        );
     524        $metadata      = wp_get_attachment_metadata( $attachment_id );
     525        $this->assertStringEndsWith( '-scaled.jpg', get_attached_file( $attachment_id ) );
     526        $this->assertArrayHasKey( 'image/webp', $metadata['sizes']['medium']['sources'] );
     527        $this->assertStringEndsNotWith( '-scaled.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] );
     528        $this->assertStringEndsWith( '-300x200.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] );
     529    }
     530
     531    /**
     532     * Test removing the generated webp images when the attachment is deleted.
     533     *
     534     * @ticket 55443
     535     */
     536    public function it_should_remove_the_generated_webp_images_when_the_attachment_is_deleted() {
     537        // Make sure no editor is available.
     538        $attachment_id = $this->factory->attachment->create_upload_object(
     539            DIR_TESTDATA . '/images/test-image.jpg'
     540        );
     541
     542        $file    = get_attached_file( $attachment_id, true );
     543        $dirname = pathinfo( $file, PATHINFO_DIRNAME );
     544
     545        $this->assertIsString( $file );
     546        $this->assertFileExists( $file );
     547
     548        $metadata = wp_get_attachment_metadata( $attachment_id );
     549        $sizes    = array( 'thumbnail', 'medium' );
     550
     551        foreach ( $sizes as $size_name ) {
     552            $this->assertArrayHasKey( 'image/webp', $metadata['sizes'][ $size_name ]['sources'] );
     553            $this->assertArrayHasKey( 'file', $metadata['sizes'][ $size_name ]['sources']['image/webp'] );
     554            $this->assertFileExists(
     555                path_join( $dirname, $metadata['sizes'][ $size_name ]['sources']['image/webp']['file'] )
     556            );
     557        }
     558
     559        wp_delete_attachment( $attachment_id );
     560
     561        foreach ( $sizes as $size_name ) {
     562            $this->assertFileDoesNotExist(
     563                path_join( $dirname, $metadata['sizes'][ $size_name ]['sources']['image/webp']['file'] )
     564            );
     565        }
     566    }
     567
     568    /**
     569     * Test removing the attached WebP version if the attachment is force deleted but empty trash day is not defined.
     570     *
     571     * @ticket 55443
     572     */
     573    public function it_should_remove_the_attached_webp_version_if_the_attachment_is_force_deleted_but_empty_trash_day_is_not_defined() {
     574        // Make sure no editor is available.
     575        $attachment_id = $this->factory->attachment->create_upload_object(
     576            DIR_TESTDATA . '/images/test-image.jpg'
     577        );
     578
     579        $file    = get_attached_file( $attachment_id, true );
     580        $dirname = pathinfo( $file, PATHINFO_DIRNAME );
     581
     582        $this->assertIsString( $file );
     583        $this->assertFileExists( $file );
     584
     585        $metadata = wp_get_attachment_metadata( $attachment_id );
     586
     587        $this->assertFileExists(
     588            path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] )
     589        );
     590
     591        wp_delete_attachment( $attachment_id, true );
     592
     593        $this->assertFileDoesNotExist(
     594            path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] )
     595        );
     596    }
     597
     598    /**
     599     * Test removing the WebP version of the image if the image is force deleted and empty trash days is set to zero.
     600     *
     601     * @ticket 55443
     602     */
     603    public function it_should_remove_the_webp_version_of_the_image_if_the_image_is_force_deleted_and_empty_trash_days_is_set_to_zero() {
     604        // Make sure no editor is available.
     605        $attachment_id = $this->factory->attachment->create_upload_object(
     606            DIR_TESTDATA . '/images/test-image.jpg'
     607        );
     608
     609        $file    = get_attached_file( $attachment_id, true );
     610        $dirname = pathinfo( $file, PATHINFO_DIRNAME );
     611
     612        $this->assertIsString( $file );
     613        $this->assertFileExists( $file );
     614
     615        $metadata = wp_get_attachment_metadata( $attachment_id );
     616
     617        $this->assertFileExists(
     618            path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] )
     619        );
     620
     621        define( 'EMPTY_TRASH_DAYS', 0 );
     622
     623        wp_delete_attachment( $attachment_id, true );
     624
     625        $this->assertFileDoesNotExist(
     626            path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] )
     627        );
     628    }
     629
     630    /**
     631     * Test avoiding the change of URLs of images that are not part of the media library.
     632     *
     633     * @ticket 55443
     634     */
     635    public function it_should_avoid_the_change_of_urls_of_images_that_are_not_part_of_the_media_library() {
     636        $paragraph = '<p>Donec accumsan, sapien et <img src="https://ia600200.us.archive.org/16/items/SPD-SLRSY-1867/hubblesite_2001_06.jpg">, id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.</p>';
     637
     638        $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) );
     639    }
     640
     641    /**
     642     * Test avoiding replacing not existing attachment IDs.
     643     *
     644     * @ticket 55443
     645     */
     646    public function it_should_avoid_replacing_not_existing_attachment_i_ds() {
     647        $paragraph = '<p>Donec accumsan, sapien et <img class="wp-image-0" src="https://ia600200.us.archive.org/16/items/SPD-SLRSY-1867/hubblesite_2001_06.jpg">, id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.</p>';
     648
     649        $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) );
     650    }
     651
     652    /**
     653     * Test preventing replacing a WebP image.
     654     *
     655     * @ticket 55443
     656     */
     657    public function it_should_test_preventing_replacing_a_webp_image() {
     658        $attachment_id = $this->factory->attachment->create_upload_object(
     659            DIR_TESTDATA . '/images/webp-lossy.webp'
     660        );
     661
     662        $tag = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) );
     663
     664        $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) );
     665    }
     666
     667    /**
     668     * Test preventing replacing a jpg image if the image does not have the target class name.
     669     *
     670     * @ticket 55443
     671     */
     672    public function it_should_test_preventing_replacing_a_jpg_image_if_the_image_does_not_have_the_target_class_name() {
     673        $attachment_id = $this->factory->attachment->create_upload_object(
     674            DIR_TESTDATA . '/images/test-image.jpg'
     675        );
     676
     677        $tag = wp_get_attachment_image( $attachment_id, 'medium' );
     678
     679        $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) );
     680    }
     681
     682    /**
     683     * Test replacing the references to a JPG image to a WebP version.
     684     *
     685     * @dataProvider provider_replace_images_with_different_extensions
     686     *
     687     * @ticket 55443
     688     */
     689    public function it_should_replace_the_references_to_a_jpg_image_to_a_webp_version( $image_path ) {
     690        $attachment_id = $this->factory->attachment->create_upload_object( $image_path );
     691
     692        $tag          = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) );
     693        $expected_tag = $tag;
     694        $metadata     = wp_get_attachment_metadata( $attachment_id );
     695        foreach ( $metadata['sizes'] as $size => $properties ) {
     696            $expected_tag = str_replace( $properties['sources']['image/jpeg']['file'], $properties['sources']['image/webp']['file'], $expected_tag );
     697        }
     698
     699        $this->assertNotEmpty( $expected_tag );
     700        $this->assertNotSame( $tag, $expected_tag );
     701        $this->assertSame( $expected_tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) );
     702    }
     703
     704    public function provider_replace_images_with_different_extensions() {
     705        yield 'An image with a .jpg extension' => array( DIR_TESTDATA . '/images/test-image.jpg' );
     706        yield 'An image with a .jpeg extension' => array( DIR_TESTDATA . '/images/test-image.jpeg' );
     707    }
     708
     709    /**
     710     * Test the full image size from the original mime type.
     711     *
     712     * @ticket 55443
     713     */
     714    public function it_should_contain_the_full_image_size_from_the_original_mime() {
     715        $attachment_id = $this->factory->attachment->create_upload_object(
     716            DIR_TESTDATA . '/images/test-image.jpg'
     717        );
     718
     719        $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) );
     720
     721        $expected = array(
     722            'ext'  => 'jpg',
     723            'type' => 'image/jpeg',
     724        );
     725        $this->assertSame( $expected, wp_check_filetype( get_attached_file( $attachment_id ) ) );
     726        $this->assertContains( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) );
     727    }
     728
     729    /**
     730     * Test preventing replacing an image with no available sources.
     731     *
     732     * @ticket 55443
     733     */
     734    public function it_should_prevent_replacing_an_image_with_no_available_sources() {
     735        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     736
     737        $attachment_id = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg' );
     738
     739        $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) );
     740        $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) );
     741    }
     742
     743    /**
     744     * Test preventing update not supported images with no available sources.
     745     *
     746     * @dataProvider provider_it_should_prevent_update_not_supported_images_with_no_available_sources
     747     *
     748     * @ticket 55443
     749     */
     750    public function it_should_prevent_update_not_supported_images_with_no_available_sources( $image_path ) {
     751        $attachment_id = $this->factory->attachment->create_upload_object( $image_path );
     752
     753        $this->assertIsNumeric( $attachment_id );
     754        $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) );
     755
     756        $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) );
     757    }
     758
     759    /**
     760     * Data provider for it_should_prevent_update_not_supported_images_with_no_available_sources.
     761     */
     762    public function provider_it_should_prevent_update_not_supported_images_with_no_available_sources() {
     763        yield 'PNG image' => array( DIR_TESTDATA . '/images/test-image.png' );
     764        yield 'GIFT image' => array( DIR_TESTDATA . '/images/test-image.gif' );
     765    }
     766
    365767}
  • trunk/tests/phpunit/tests/image/functions.php

    r53542 r53751  
    640640        }
    641641
     642        // Use legacy JPEG output.
     643        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     644
    642645        $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf';
    643646        $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf';
     
    678681                    'mime-type' => 'image/jpeg',
    679682                    'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ),
     683                    'sources'   => array(
     684                        'image/jpeg' => array(
     685                            'file'     => 'wordpress-gsoc-flyer-pdf-232x300.jpg',
     686                            'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ),
     687                        ),
     688                    ),
    680689                ),
    681690                'large'     => array(
     
    685694                    'mime-type' => 'image/jpeg',
    686695                    'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ),
     696                    'sources'   => array(
     697                        'image/jpeg' => array(
     698                            'file'     => 'wordpress-gsoc-flyer-pdf-791x1024.jpg',
     699                            'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ),
     700                        ),
     701                    ),
    687702                ),
    688703                'thumbnail' => array(
     
    692707                    'mime-type' => 'image/jpeg',
    693708                    'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ),
     709                    'sources'   => array(
     710                        'image/jpeg' => array(
     711                            'file'     => 'wordpress-gsoc-flyer-pdf-116x150.jpg',
     712                            'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ),
     713                        ),
     714                    ),
    694715                ),
    695716            ),
     
    703724            unlink( $temp_dir . $size['file'] );
    704725        }
     726        remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
    705727    }
    706728
     
    716738
    717739        update_option( 'medium_crop', 1 );
     740
     741        // Use legacy JPEG output.
     742        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
    718743
    719744        $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf';
     
    755780                    'mime-type' => 'image/jpeg',
    756781                    'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ),
     782                    'sources'   => array(
     783                        'image/jpeg' => array(
     784                            'file'     => 'wordpress-gsoc-flyer-pdf-300x300.jpg',
     785                            'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ),
     786                        ),
     787                    ),
    757788                ),
    758789                'large'     => array(
     
    762793                    'mime-type' => 'image/jpeg',
    763794                    'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ),
     795                    'sources'   => array(
     796                        'image/jpeg' => array(
     797                            'file'     => 'wordpress-gsoc-flyer-pdf-791x1024.jpg',
     798                            'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ),
     799                        ),
     800                    ),
     801
    764802                ),
    765803                'thumbnail' => array(
     
    769807                    'mime-type' => 'image/jpeg',
    770808                    'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ),
     809                    'sources'   => array(
     810                        'image/jpeg' => array(
     811                            'file'     => 'wordpress-gsoc-flyer-pdf-116x150.jpg',
     812                            'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ),
     813                        ),
     814                    ),
    771815                ),
    772816            ),
     
    780824            unlink( $temp_dir . $size['file'] );
    781825        }
     826        remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     827
    782828    }
    783829
     
    789835            $this->markTestSkipped( 'Rendering PDFs is not supported on this system.' );
    790836        }
     837
     838        // Use legacy JPEG output.
     839        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
    791840
    792841        $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf';
     
    822871            'mime-type' => 'image/jpeg',
    823872            'filesize'  => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ),
     873            'sources'   => array(
     874                'image/jpeg' => array(
     875                    'file'     => 'wordpress-gsoc-flyer-pdf-77x100.jpg',
     876                    'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ),
     877                ),
     878            ),
    824879        );
    825880
     
    837892            unlink( $temp_dir . $size['file'] );
    838893        }
     894        remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
    839895    }
    840896
     
    10271083        );
    10281084    }
     1085
     1086    /**
     1087     * @ticket 55443
     1088     */
     1089    public function test_wp_upload_image_mime_transforms_generates_webp_and_jpeg_for_both_by_default() {
     1090        $result = wp_upload_image_mime_transforms( 42 );
     1091        $this->assertArrayHasKey( 'image/jpeg', $result );
     1092        $this->assertArrayHasKey( 'image/webp', $result );
     1093        $this->assertSameSets( array( 'image/jpeg', 'image/webp' ), $result['image/jpeg'] );
     1094        $this->assertSameSets( array( 'image/jpeg', 'image/webp' ), $result['image/webp'] );
     1095    }
     1096
     1097    /**
     1098     * @ticket 55443
     1099     */
     1100    public function test_wp_upload_image_mime_transforms_filter_always_use_webp_instead_of_jpeg() {
     1101        add_filter(
     1102            'wp_upload_image_mime_transforms',
     1103            function( $transforms ) {
     1104                // Ensure JPG only results in WebP files.
     1105                $transforms['image/jpeg'] = array( 'image/webp' );
     1106                // Unset WebP since it does not need any transformation in that case.
     1107                unset( $transforms['image/webp'] );
     1108                return $transforms;
     1109            }
     1110        );
     1111
     1112        $result = wp_upload_image_mime_transforms( 42 );
     1113        $this->assertArrayHasKey( 'image/jpeg', $result );
     1114        $this->assertArrayNotHasKey( 'image/webp', $result );
     1115        $this->assertSameSets( array( 'image/webp' ), $result['image/jpeg'] );
     1116    }
     1117
     1118    /**
     1119     * @ticket 55443
     1120     */
     1121    public function test_wp_upload_image_mime_transforms_filter_receives_parameters() {
     1122        $attachment_id = null;
     1123        add_filter(
     1124            'wp_upload_image_mime_transforms',
     1125            function( $transforms, $param1 ) use ( &$attachment_id ) {
     1126                $attachment_id = $param1;
     1127                return $transforms;
     1128            },
     1129            10,
     1130            2
     1131        );
     1132
     1133        wp_upload_image_mime_transforms( 23 );
     1134        $this->assertSame( 23, $attachment_id );
     1135    }
     1136
     1137    /**
     1138     * @ticket 55443
     1139     */
     1140    public function test_wp_upload_image_mime_transforms_filter_with_empty_array() {
     1141        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     1142        $result = wp_upload_image_mime_transforms( 42 );
     1143        $this->assertSame( array(), $result );
     1144    }
     1145
     1146    /**
     1147     * @ticket 55443
     1148     */
     1149    public function test_wp_upload_image_mime_transforms_filter_with_invalid_usage() {
     1150        $default = wp_upload_image_mime_transforms( 42 );
     1151
     1152        add_filter( 'wp_upload_image_mime_transforms', '__return_false' );
     1153        $result = wp_upload_image_mime_transforms( 42 );
     1154        $this->assertSame( $default, $result );
     1155    }
     1156
     1157    /**
     1158     * @ticket 55443
     1159     */
     1160    public function test__wp_get_primary_and_additional_mime_types_default() {
     1161        $jpeg_file = DIR_TESTDATA . '/images/test-image-large.jpg';
     1162
     1163        list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $jpeg_file, 42 );
     1164        $this->assertSame( 'image/jpeg', $primary_mime_type );
     1165
     1166        // WebP may not be supported by the server, in which case it will be stripped from the results.
     1167        if ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     1168            $this->assertSame( array( 'image/webp' ), $additional_mime_types );
     1169        } else {
     1170            $this->assertSame( array(), $additional_mime_types );
     1171        }
     1172    }
     1173
     1174    /**
     1175     * @ticket 55443
     1176     */
     1177    public function test__wp_get_primary_and_additional_mime_types_prefer_original_mime() {
     1178        $jpeg_file = DIR_TESTDATA . '/images/test-image-large.jpg';
     1179
     1180        // Set 'image/jpeg' only as secondary output MIME type.
     1181        // Still, because it is the original, it should be chosen as primary over 'image/webp'.
     1182        add_filter(
     1183            'wp_upload_image_mime_transforms',
     1184            function( $transforms ) {
     1185                $transforms['image/jpeg'] = array( 'image/webp', 'image/jpeg' );
     1186                return $transforms;
     1187            }
     1188        );
     1189
     1190        list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $jpeg_file, 42 );
     1191        $this->assertSame( 'image/jpeg', $primary_mime_type );
     1192
     1193        // WebP may not be supported by the server, in which case it will be stripped from the results.
     1194        if ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     1195            $this->assertSame( array( 'image/webp' ), $additional_mime_types );
     1196        } else {
     1197            $this->assertSame( array(), $additional_mime_types );
     1198        }
     1199    }
     1200
     1201    /**
     1202     * @ticket 55443
     1203     */
     1204    public function test__wp_get_primary_and_additional_mime_types_use_original_mime_when_no_transformation_rules() {
     1205        $jpeg_file = DIR_TESTDATA . '/images/test-image-large.jpg';
     1206
     1207        // Strip all transformation rules.
     1208        add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );
     1209
     1210        list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $jpeg_file, 42 );
     1211        $this->assertSame( 'image/jpeg', $primary_mime_type );
     1212        $this->assertSame( array(), $additional_mime_types );
     1213    }
     1214
     1215    /**
     1216     * @ticket 55443
     1217     */
     1218    public function test__wp_get_primary_and_additional_mime_types_different_output_mime() {
     1219        $jpeg_file = DIR_TESTDATA . '/images/test-image-large.jpg';
     1220
     1221        // Set 'image/webp' as the only output MIME type.
     1222        // In that case, JPEG is not generated at all, so WebP becomes the primary MIME type.
     1223        add_filter(
     1224            'wp_upload_image_mime_transforms',
     1225            function( $transforms ) {
     1226                $transforms['image/jpeg'] = array( 'image/webp' );
     1227                return $transforms;
     1228            }
     1229        );
     1230
     1231        list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $jpeg_file, 42 );
     1232
     1233        // WebP may not be supported by the server, in which case it will fall back to the original MIME type.
     1234        if ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     1235            $this->assertSame( 'image/webp', $primary_mime_type );
     1236        } else {
     1237            $this->assertSame( 'image/jpeg', $primary_mime_type );
     1238        }
     1239
     1240        $this->assertSame( array(), $additional_mime_types );
     1241    }
     1242
     1243    /**
     1244     * @ticket 55443
     1245     */
     1246    public function test__wp_get_primary_and_additional_mime_types_different_output_mimes() {
     1247        $jpeg_file = DIR_TESTDATA . '/images/test-image-large.jpg';
     1248
     1249        // Set 'image/webp' and 'image/avif' as output MIME types.
     1250        // In that case, JPEG is not generated at all, with WebP being the primary MIME type and AVIF the secondary.
     1251        add_filter(
     1252            'wp_upload_image_mime_transforms',
     1253            function( $transforms ) {
     1254                $transforms['image/jpeg'] = array( 'image/webp', 'image/avif' );
     1255                return $transforms;
     1256            }
     1257        );
     1258
     1259        list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $jpeg_file, 42 );
     1260
     1261        // WebP may not be supported by the server, in which case it will fall back to the original MIME type.
     1262        if ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     1263            $this->assertSame( 'image/webp', $primary_mime_type );
     1264        } else {
     1265            $this->assertSame( 'image/jpeg', $primary_mime_type );
     1266        }
     1267
     1268        // AVIF may not be supported by the server, in which case it will be stripped from the results.
     1269        if ( wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
     1270            $this->assertSame( array( 'image/avif' ), $additional_mime_types );
     1271        } else {
     1272            $this->assertSame( array(), $additional_mime_types );
     1273        }
     1274    }
     1275
     1276    /**
     1277     * @ticket 55443
     1278     * @dataProvider data__wp_filter_image_sizes_additional_mime_type_support
     1279     */
     1280    public function test__wp_filter_image_sizes_additional_mime_type_support( $input_size_data, $filter_callback, $expected_size_names ) {
     1281        remove_all_filters( 'wp_image_sizes_with_additional_mime_type_support' );
     1282        if ( $filter_callback ) {
     1283            add_filter( 'wp_image_sizes_with_additional_mime_type_support', $filter_callback );
     1284        }
     1285
     1286        $expected_size_data = array_intersect_key( $input_size_data, array_flip( $expected_size_names ) );
     1287
     1288        $output_size_data = _wp_filter_image_sizes_additional_mime_type_support( $input_size_data, 42 );
     1289        $this->assertEqualSetsWithIndex( $expected_size_data, $output_size_data );
     1290    }
     1291
     1292    public function data__wp_filter_image_sizes_additional_mime_type_support() {
     1293        $thumbnail_data    = array(
     1294            'width'  => 150,
     1295            'height' => 150,
     1296            'crop'   => true,
     1297        );
     1298        $medium_data       = array(
     1299            'width'  => 300,
     1300            'height' => 300,
     1301            'crop'   => false,
     1302        );
     1303        $medium_large_data = array(
     1304            'width'  => 768,
     1305            'height' => 0,
     1306            'crop'   => false,
     1307        );
     1308        $large_data        = array(
     1309            'width'  => 1024,
     1310            'height' => 1024,
     1311            'crop'   => false,
     1312        );
     1313        $custom_data       = array(
     1314            'width'  => 512,
     1315            'height' => 512,
     1316            'crop'   => true,
     1317        );
     1318
     1319        return array(
     1320            array(
     1321                array(
     1322                    'thumbnail'    => $thumbnail_data,
     1323                    'medium'       => $medium_data,
     1324                    'medium_large' => $medium_large_data,
     1325                    'large'        => $large_data,
     1326                ),
     1327                null,
     1328                array( 'thumbnail', 'medium', 'medium_large', 'large' ),
     1329            ),
     1330            array(
     1331                array(
     1332                    'thumbnail' => $thumbnail_data,
     1333                    'medium'    => $medium_data,
     1334                    'custom'    => $custom_data,
     1335                ),
     1336                null,
     1337                array( 'thumbnail', 'medium' ),
     1338            ),
     1339            array(
     1340                array(
     1341                    'thumbnail'    => $thumbnail_data,
     1342                    'medium'       => $medium_data,
     1343                    'medium_large' => $medium_large_data,
     1344                    'large'        => $large_data,
     1345                ),
     1346                function( $enabled_sizes ) {
     1347                    unset( $enabled_sizes['medium_large'], $enabled_sizes['large'] );
     1348                    return $enabled_sizes;
     1349                },
     1350                array( 'thumbnail', 'medium' ),
     1351            ),
     1352            array(
     1353                array(
     1354                    'thumbnail'    => $thumbnail_data,
     1355                    'medium'       => $medium_data,
     1356                    'medium_large' => $medium_large_data,
     1357                    'large'        => $large_data,
     1358                ),
     1359                function( $enabled_sizes ) {
     1360                    $enabled_sizes['medium_large'] = false;
     1361                    $enabled_sizes['large']        = false;
     1362                    return $enabled_sizes;
     1363                },
     1364                array( 'thumbnail', 'medium' ),
     1365            ),
     1366            array(
     1367                array(
     1368                    'thumbnail' => $thumbnail_data,
     1369                    'medium'    => $medium_data,
     1370                    'custom'    => $custom_data,
     1371                ),
     1372                function( $enabled_sizes ) {
     1373                    unset( $enabled_sizes['medium'] );
     1374                    $enabled_sizes['custom'] = true;
     1375                    return $enabled_sizes;
     1376                },
     1377                array( 'thumbnail', 'custom' ),
     1378            ),
     1379        );
     1380    }
     1381
     1382    /**
     1383     * Test the `_wp_maybe_scale_and_rotate_image()` function.
     1384     *
     1385     * @dataProvider data_test__wp_maybe_scale_and_rotate_image
     1386     *
     1387     * @ticket 55443
     1388     */
     1389    public function test__wp_maybe_scale_and_rotate_image( $file, $imagesize, $mime_type, $expected ) {
     1390        if ( ! wp_image_editor_supports( array( 'mime_type' => $mime_type ) ) ) {
     1391            $this->markTestSkipped( sprintf( 'This test requires %s support.', $mime_type ) );
     1392        }
     1393
     1394        $attributes    = array( 'post_mime_type' => $mime_type );
     1395        $attachment_id = $this->factory->attachment->create_object( $file, 0, $attributes );
     1396        $exif_meta     = wp_read_image_metadata( $file );
     1397
     1398        list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type );
     1399
     1400        $this->assertSame( $expected['rotated'], $rotated );
     1401        $this->assertSame( $expected['resized'], $resized );
     1402        $this->assertSame( $expected['size'], $editor->get_size() );
     1403    }
     1404
     1405    /**
     1406     * Data provider for the `test__wp_maybe_scale_and_rotate_image()` test.
     1407     *
     1408     * @return array
     1409     */
     1410    public function data_test__wp_maybe_scale_and_rotate_image() {
     1411        return array(
     1412
     1413            // Image that will be scaled.
     1414            array(
     1415                DIR_TESTDATA . '/images/test-image-large.jpg',
     1416                array( 3000, 2250 ),
     1417                'image/jpeg',
     1418                array(
     1419                    'rotated' => false,
     1420                    'resized' => true,
     1421                    'size'    => array(
     1422                        'width'  => 2560,
     1423                        'height' => 1920,
     1424                    ),
     1425                ),
     1426            ),
     1427
     1428            // Image that will not be scaled.
     1429            array(
     1430                DIR_TESTDATA . '/images/canola.jpg',
     1431                array( 640, 480 ),
     1432                'image/jpeg',
     1433                array(
     1434                    'rotated' => false,
     1435                    'resized' => false,
     1436                    'size'    => array(
     1437                        'width'  => 640,
     1438                        'height' => 480,
     1439                    ),
     1440                ),
     1441            ),
     1442
     1443            // Image that will be flipped.
     1444            array(
     1445                DIR_TESTDATA . '/images/test-image-upside-down.jpg',
     1446                array( 600, 450 ),
     1447                'image/jpeg',
     1448                array(
     1449                    'rotated' => true,
     1450                    'resized' => false,
     1451                    'size'    => array(
     1452                        'width'  => 600,
     1453                        'height' => 450,
     1454                    ),
     1455                ),
     1456            ),
     1457
     1458            // Image that will be rotated.
     1459            array(
     1460                DIR_TESTDATA . '/images/test-image-rotated-90ccw.jpg',
     1461                array( 1200, 1800 ),
     1462                'image/jpeg',
     1463                array(
     1464                    'rotated' => true,
     1465                    'resized' => false,
     1466                    'size'    => array(
     1467                        'width'  => 1800,
     1468                        'height' => 1200,
     1469                    ),
     1470                ),
     1471            ),
     1472
     1473            // Image that will not be rotated - WebP Exif is not supported in PHP.
     1474            array(
     1475                DIR_TESTDATA . '/images/test-image-rotated-90cw.webp',
     1476                array( 1024, 768 ),
     1477                'image/webp',
     1478                array(
     1479                    'rotated' => false,
     1480                    'resized' => false,
     1481                    'size'    => array(
     1482                        'width'  => 1024,
     1483                        'height' => 768,
     1484                    ),
     1485                ),
     1486            ),
     1487
     1488        );
     1489    }
     1490
     1491    /**
     1492     * Test the `_wp_get_image_suffix()` function.
     1493     * @dataProvider data_test__wp_get_image_suffix
     1494     *
     1495     * @ticket 55443
     1496     */
     1497    public function test__wp_get_image_suffix( $resized, $rotated, $expected ) {
     1498        $this->assertSame( $expected, _wp_get_image_suffix( $resized, $rotated ) );
     1499    }
     1500
     1501    /**
     1502     * Data provider for the `test__wp_get_image_suffix()` test.
     1503     */
     1504    public function data_test__wp_get_image_suffix() {
     1505        return array(
     1506            array( false, false, '' ),
     1507            array( true, false, 'scaled' ),
     1508            array( false, true, 'rotated' ),
     1509            array( true, true, 'scaled' ),
     1510        );
     1511    }
    10291512}
  • trunk/tests/phpunit/tests/media.php

    r53558 r53751  
    22522252        add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
    22532253        add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
     2254        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    22542255
    22552256        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
     
    22572258        remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
    22582259        remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
     2260        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
     2261
    22592262    }
    22602263
     
    22902293        $img = wp_img_tag_add_decoding_attr( $img, 'the_content' );
    22912294        $img = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . 'srcset="image2x.jpg 2x" />', $img );
     2295        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    22922296
    22932297        // The content filter should return the image unchanged.
    22942298        $this->assertSame( $img, wp_filter_content_tags( $img ) );
     2299
     2300        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
    22952301    }
    22962302
     
    23622368        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
    23632369        add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' );
     2370        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    23642371
    23652372        add_filter(
     
    24242431     */
    24252432    public function test_wp_filter_content_tags_schemes() {
     2433        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    24262434        $image_meta = wp_get_attachment_metadata( self::$large_id );
    24272435        $size_array = $this->get_image_size_array_from_meta( $image_meta, 'medium' );
     
    24692477
    24702478        $this->assertSame( $expected, $actual );
     2479        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
    24712480    }
    24722481
     
    29622971        add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
    29632972        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     2973        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    29642974
    29652975        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
     
    29672977        remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
    29682978        remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     2979        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
    29692980    }
    29702981
     
    30423053        add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
    30433054        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3055        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    30443056
    30453057        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
     
    30473059        remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
    30483060        remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3061        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
    30493062    }
    30503063
     
    30753088        add_filter( 'wp_lazy_loading_enabled', '__return_true' );
    30763089
     3090        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
     3091
    30773092        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
    30783093        remove_filter( 'wp_lazy_loading_enabled', '__return_true' );
    30793094        remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3095        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
     3096
    30803097    }
    30813098
     
    31023119        add_filter( 'wp_lazy_loading_enabled', '__return_false' );
    31033120
     3121        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
     3122
    31043123        $this->assertSame( $content, wp_filter_content_tags( $content ) );
    31053124        remove_filter( 'wp_lazy_loading_enabled', '__return_false' );
    31063125        remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3126        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
    31073127    }
    31083128
     
    35303550    function test_wp_filter_content_tags_with_wp_get_loading_attr_default() {
    35313551        global $wp_query, $wp_the_query;
     3552        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
    35323553
    35333554        $img1         = get_image_tag( self::$large_id, '', '', '', 'large' );
     
    35653586            remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
    35663587        }
     3588        remove_filter( 'wp_content_image_mimes', '__return_empty_array' );
    35673589
    35683590        // After filtering, the first image should not be lazy-loaded while the other ones should be.
     
    36143636        remove_filter( 'wp_omit_loading_attr_threshold', '__return_null', 100 );
    36153637    }
     3638
     3639    /**
     3640     * @ticket 55443
     3641     */
     3642    public function test_wp_image_use_alternate_mime_types_replaces_jpg_with_webp_where_available() {
     3643        if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     3644            $this->markTestSkipped( 'This test requires WebP support.' );
     3645        }
     3646
     3647        // The attachment $large_id is a JPEG image, so it gets WebP files generated by default.
     3648        $tag          = wp_get_attachment_image( self::$large_id, 'full' );
     3649        $expected_tag = $tag;
     3650
     3651        $metadata = wp_get_attachment_metadata( self::$large_id );
     3652        foreach ( $metadata['sizes'] as $size => $properties ) {
     3653            // Some sizes may not have WebP if the WebP file is larger than the JPEG for the size.
     3654            if ( ! isset( $properties['sources']['image/webp'] ) ) {
     3655                continue;
     3656            }
     3657            $expected_tag = str_replace( $properties['sources']['image/jpeg']['file'], $properties['sources']['image/webp']['file'], $expected_tag );
     3658        }
     3659        // Same applies to the full size.
     3660        if ( isset( $metadata['sources']['image/webp'] ) ) {
     3661            $expected_tag = str_replace( $metadata['sources']['image/jpeg']['file'], $metadata['sources']['image/webp']['file'], $expected_tag );
     3662        }
     3663
     3664        $this->assertNotSame( $tag, $expected_tag );
     3665        $this->assertSame( $expected_tag, wp_image_use_alternate_mime_types( $tag, 'the_content', self::$large_id ) );
     3666    }
     3667
     3668    /**
     3669     * @ticket 55443
     3670     */
     3671    public function test_wp_image_use_alternate_mime_types_does_not_replace_jpg_when_webp_is_not_available() {
     3672        if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     3673            $this->markTestSkipped( 'This test requires WebP support.' );
     3674        }
     3675
     3676        // The attachment $large_id is a JPEG image, so it gets WebP files generated by default.
     3677        $tag = wp_get_attachment_image( self::$large_id, 'full' );
     3678
     3679        // Update attachment metadata as if the image had no WebP available for any sub-sizes and the full size.
     3680        $metadata = wp_get_attachment_metadata( self::$large_id );
     3681        foreach ( $metadata['sizes'] as $size => $properties ) {
     3682            unset( $metadata['sizes'][ $size ]['sources']['image/webp'] );
     3683        }
     3684        unset( $metadata['sources']['image/webp'] );
     3685        wp_update_attachment_metadata( self::$large_id, $metadata );
     3686
     3687        $this->assertSame( $tag, wp_image_use_alternate_mime_types( $tag, 'the_content', self::$large_id ) );
     3688    }
     3689
     3690    /**
     3691     * @ticket 55443
     3692     */
     3693    public function test_wp_image_use_alternate_mime_types_still_replaces_jpg_subsizes_when_webp_is_not_available_for_full_size() {
     3694        if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     3695            $this->markTestSkipped( 'This test requires WebP support.' );
     3696        }
     3697
     3698        // The attachment $large_id is a JPEG image, so it gets WebP files generated by default.
     3699        $tag          = wp_get_attachment_image( self::$large_id, 'full' );
     3700        $expected_tag = $tag;
     3701
     3702        // Update attachment metadata as if the image had no WebP available for the full size.
     3703        $metadata = wp_get_attachment_metadata( self::$large_id );
     3704        unset( $metadata['sources']['image/webp'] );
     3705        wp_update_attachment_metadata( self::$large_id, $metadata );
     3706
     3707        foreach ( $metadata['sizes'] as $size => $properties ) {
     3708            // Some sizes may not have WebP if the WebP file is larger than the JPEG for the size.
     3709            if ( ! isset( $properties['sources']['image/webp'] ) ) {
     3710                continue;
     3711            }
     3712            $expected_tag = str_replace( $properties['sources']['image/jpeg']['file'], $properties['sources']['image/webp']['file'], $expected_tag );
     3713        }
     3714
     3715        $this->assertNotSame( $tag, $expected_tag );
     3716        $this->assertSame( $expected_tag, wp_image_use_alternate_mime_types( $tag, 'the_content', self::$large_id ) );
     3717    }
     3718
     3719    /**
     3720     * @ticket 55443
     3721     */
     3722    public function test_wp_image_use_alternate_mime_types_respects_wp_content_image_mimes_filter() {
     3723        if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
     3724            $this->markTestSkipped( 'This test requires WebP support.' );
     3725        }
     3726
     3727        // The attachment $large_id is a JPEG image, so it gets WebP files generated by default.
     3728        $tag = wp_get_attachment_image( self::$large_id, 'full' );
     3729
     3730        // Invalid filter value results in no changes to content.
     3731        add_filter( 'wp_content_image_mimes', '__return_false' );
     3732        $this->assertSame( $tag, wp_image_use_alternate_mime_types( $tag, 'the_content', self::$large_id ) );
     3733
     3734        // Empty array results in no changes to content.
     3735        add_filter( 'wp_content_image_mimes', '__return_empty_array' );
     3736        $this->assertSame( $tag, wp_image_use_alternate_mime_types( $tag, 'the_content', self::$large_id ) );
     3737
     3738        // Preferring JPEG over WebP results in no changes to content.
     3739        add_filter(
     3740            'wp_content_image_mimes',
     3741            function() {
     3742                return array( 'image/jpeg', 'image/webp' );
     3743            }
     3744        );
     3745        $this->assertSame( $tag, wp_image_use_alternate_mime_types( $tag, 'the_content', self::$large_id ) );
     3746    }
     3747
     3748    /**
     3749     * @ticket 55443
     3750     */
     3751    public function test__wp_in_front_end_context_without_wp_query() {
     3752        unset( $GLOBALS['wp_query'] );
     3753
     3754        $this->assertFalse( _wp_in_front_end_context() );
     3755    }
     3756
     3757    /**
     3758     * @ticket 55443
     3759     */
     3760    public function test__wp_in_front_end_context_with_feed() {
     3761        remove_all_actions( 'template_redirect' );
     3762        do_action( 'template_redirect' );
     3763        $GLOBALS['wp_query']->is_feed = true;
     3764
     3765        $this->assertFalse( _wp_in_front_end_context() );
     3766    }
     3767
     3768    /**
     3769     * @ticket 55443
     3770     */
     3771    public function test__wp_in_front_end_context_before_and_after_template_redirect() {
     3772        $result = _wp_in_front_end_context();
     3773
     3774        remove_all_actions( 'template_redirect' );
     3775        do_action( 'template_redirect' );
     3776
     3777        $this->assertFalse( $result );
     3778        $this->assertTrue( _wp_in_front_end_context() );
     3779    }
     3780
     3781    /**
     3782     * @ticket 55443
     3783     */
     3784    public function test__wp_in_front_end_context_within_wp_head() {
     3785        remove_all_actions( 'template_redirect' );
     3786        do_action( 'template_redirect' );
     3787
     3788        // Call function within a 'wp_head' callback.
     3789        remove_all_actions( 'wp_head' );
     3790        $result = null;
     3791        add_action(
     3792            'wp_head',
     3793            function() use ( &$result ) {
     3794                $result = _wp_in_front_end_context();
     3795            }
     3796        );
     3797        do_action( 'wp_head' );
     3798
     3799        $this->assertFalse( $result );
     3800    }
    36163801}
    36173802
Note: See TracChangeset for help on using the changeset viewer.