Make WordPress Core


Ignore:
Timestamp:
06/15/2019 01:01:48 AM (7 years ago)
Author:
azaozz
Message:

Save progress of intermediate image creation after upload. First run.

  • Introduces wp_get_missing_image_subsizes() and wp_update_image_subsizes() to generate image sub-sizes that are missing or were not created after the upload.
  • Adds a way to display errors that happened while creating sub-sizes.
  • Introduces wp_create_image_subsizes() intended for use after an image was uploaded. It saves/updates the image metadata immediately after each sub-size is created. This fixes the (long standing) problem when some of the sub-size image files were created but there was a timeout or an error and the metadata was not saved. Until now such uploads were considered "failed" which usually resulted in the user trying to upload the same image again, creating even more "orphan" image files.

Note that the patch also includes some unrelated WPCS fixes.

Props mikeschroder, azaozz.
See #40439.

File:
1 edited

Legend:

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

    r44785 r45538  
    6868
    6969/**
    70  * Generate post thumbnail attachment meta data.
     70 * Compare the existing image sub-sizes (as saved in the attachment meta)
     71 * to the currently registered image sub-sizes, and return the difference.
     72 *
     73 * Registered sub-sizes that are larger than the image are skipped.
     74 *
     75 * @since 5.3.0
     76 *
     77 * @param int $attachment_id The image attachment post ID.
     78 * @return array An array of the image sub-sizes that are currently defined but don't exist for this image.
     79 */
     80function wp_get_missing_image_subsizes( $attachment_id ) {
     81        if ( ! wp_attachment_is_image( $attachment_id ) ) {
     82                return array();
     83        }
     84
     85        $registered_sizes = wp_get_registered_image_subsizes();
     86        $image_meta       = wp_get_attachment_metadata( $attachment_id );
     87
     88        // Meta error?
     89        if ( empty( $image_meta ) ) {
     90                return $defined_sizes;
     91        }
     92
     93        $full_width     = (int) $image_meta['width'];
     94        $full_height    = (int) $image_meta['height'];
     95        $possible_sizes = array();
     96
     97        // Skip registered sizes that are too large for the uploaded image.
     98        foreach ( $registered_sizes as $size_name => $size_data ) {
     99                if ( image_resize_dimensions( $full_width, $full_height, $size_data['width'], $size_data['height'], $size_data['crop'] ) ) {
     100                        $possible_sizes[ $size_name ] = $size_data;
     101                }
     102        }
     103
     104        if ( empty( $image_meta['sizes'] ) ) {
     105                $image_meta['sizes'] = array();
     106        }
     107
     108        // Remove sizes that already exist. Only checks for matching "size names".
     109        // It is possible that the dimensions for a particular size name have changed.
     110        // For example the user has changed the values on the Settings -> Media screen.
     111        // However we keep the old sub-sizes with the previous dimensions
     112        // as the image may have been used in an older post.
     113        $missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] );
     114
     115        /**
     116         * Filters the array of missing image sub-sizes for an uploaded image.
     117         *
     118         * @since 5.3.0
     119         *
     120         * @param array $missing_sizes Array with the missing image sub-sizes.
     121         * @param array $image_meta    The image meta data.
     122         * @param int   $attachment_id The image attachment post ID.
     123         */
     124        return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id );
     125}
     126
     127/**
     128 * If any of the currently registered image sub-sizes are missing,
     129 * create them and update the image meta data.
     130 *
     131 * @since 5.3.0
     132 *
     133 * @param int $attachment_id The image attachment post ID.
     134 * @return array The updated image meta data array.
     135 */
     136function wp_update_image_subsizes( $attachment_id ) {
     137        $missing_sizes = wp_get_missing_image_subsizes( $attachment_id );
     138        $image_meta    = wp_get_attachment_metadata( $attachment_id );
     139
     140        if ( empty( $missing_sizes ) ) {
     141                return $image_meta;
     142        }
     143
     144        $image_file = get_attached_file( $attachment_id );
     145
     146        // This also updates the image meta.
     147        return _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id );
     148}
     149
     150/**
     151 * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata.
     152 *
     153 * Intended for use after an image is uploaded. Saves/updates the image metadata after each
     154 * sub-size is created. If there was an error, it is added to the returned image metadata array.
     155 *
     156 * @since 5.3.0
     157 *
     158 * @param string $file          Full path to the image file.
     159 * @param array  $image_meta    The attachment meta data array.
     160 * @param int    $attachment_id Attachment Id to process.
     161 * @return array The attachment metadata with updated `sizes` array. Includes an array of errors encountered while resizing.
     162 */
     163function wp_create_image_subsizes( $file, $image_meta, $attachment_id ) {
     164        if ( empty( $image_meta ) || ! isset( $image_meta['width'], $image_meta['height'] ) ) {
     165                // New uploaded image.
     166                $imagesize            = getimagesize( $file );
     167                $image_meta['width']  = $imagesize[0];
     168                $image_meta['height'] = $imagesize[1];
     169
     170                // Make the file path relative to the upload dir.
     171                $image_meta['file'] = _wp_relative_upload_path( $file );
     172
     173                // Fetch additional metadata from EXIF/IPTC.
     174                $exif_meta = wp_read_image_metadata( $file );
     175
     176                if ( $exif_meta ) {
     177                        $image_meta['image_meta'] = $exif_meta;
     178                }
     179        }
     180
     181        $new_sizes = wp_get_registered_image_subsizes();
     182
     183        /**
     184         * Filters the image sizes automatically generated when uploading an image.
     185         *
     186         * @since 2.9.0
     187         * @since 4.4.0 Added the `$image_meta` argument.
     188         * @since 5.3.0 Added the `$attachment_id` argument.
     189         *
     190         * @param array $new_sizes     Associative array of image sizes to be created.
     191         * @param array $image_meta    The image meta data: width, height, file, sizes, etc.
     192         * @param int   $attachment_id The attachment post ID for the image.
     193         */
     194        $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id );
     195
     196        return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id );
     197}
     198
     199/**
     200 * Low-level function to create image sub-sizes.
     201 *
     202 * Updates the image meta after each sub-size is created.
     203 * Errors are stored in the returned image metadata array.
     204 *
     205 * @since 5.3.0
     206 * @access private
     207 *
     208 * $padam array  $new_sizes     Array defining what sizes to create.
     209 * @param string $file          Full path to the image file.
     210 * @param array  $image_meta    The attachment meta data array.
     211 * @param int    $attachment_id Attachment Id to process.
     212 * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing.
     213 */
     214function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) {
     215        // Check if any of the new sizes already exist.
     216        if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) {
     217                foreach ( $image_meta['sizes'] as $size_name => $size_meta ) {
     218                        // Only checks "size name" so we don't override existing images even if the dimensions
     219                        // don't match the currently defined size with the same name.
     220                        // To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta.
     221                        if ( array_key_exists( $size_name, $new_sizes ) ) {
     222                                unset( $new_sizes[ $size_name ] );
     223                        }
     224                }
     225        } else {
     226                $image_meta['sizes'] = array();
     227        }
     228
     229        if ( ! empty( $new_sizes ) ) {
     230                $editor = wp_get_image_editor( $file );
     231
     232                if ( ! is_wp_error( $editor ) ) {
     233                        if ( method_exists( $editor, 'make_subsize' ) ) {
     234                                foreach ( $new_sizes as $new_size_name => $new_size_data ) {
     235                                        $new_size_meta = $editor->make_subsize( $new_size_data );
     236
     237                                        if ( is_wp_error( $new_size_meta ) ) {
     238                                                if ( empty( $image_meta['subsize_errors'] ) ) {
     239                                                        $image_meta['subsize_errors'] = array();
     240                                                }
     241
     242                                                $error = array(
     243                                                        'error_code'    => $new_size_meta->get_error_code(),
     244                                                        'error_message' => $new_size_meta->get_error_message(),
     245                                                );
     246
     247                                                // Store the error code and error message for displaying in the UI.
     248                                                $image_meta['subsize_errors'][ $new_size_name ] = $error;
     249                                        } else {
     250                                                // The sub-size was created successfully.
     251                                                // Clear out previous errors in creating this subsize.
     252                                                if ( ! empty( $image_meta['subsize_errors'][ $new_size_name ] ) ) {
     253                                                        unset( $image_meta['subsize_errors'][ $new_size_name ] );
     254                                                }
     255
     256                                                if ( empty( $image_meta['subsize_errors'] ) ) {
     257                                                        unset( $image_meta['subsize_errors'] );
     258                                                }
     259
     260                                                // Save the size meta value.
     261                                                $image_meta['sizes'][ $new_size_name ] = $new_size_meta;
     262                                        }
     263
     264                                        wp_update_attachment_metadata( $attachment_id, $image_meta );
     265                                }
     266                        } else {
     267                                // Fall back to `$editor->multi_resize()`.
     268                                $created_sizes = $editor->multi_resize( $new_sizes );
     269
     270                                if ( ! empty( $created_sizes ) ) {
     271                                        $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes );
     272                                        unset( $image_meta['subsize_errors'] );
     273                                        wp_update_attachment_metadata( $attachment_id, $image_meta );
     274                                }
     275                        }
     276                }
     277        }
     278
     279        return $image_meta;
     280}
     281
     282/**
     283 * Generate attachment meta data and create image sub-sizes for images.
    71284 *
    72285 * @since 2.1.0
     
    84297
    85298        if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) {
    86                 $imagesize          = getimagesize( $file );
    87                 $metadata['width']  = $imagesize[0];
    88                 $metadata['height'] = $imagesize[1];
    89 
    90                 // Make the file path relative to the upload dir.
    91                 $metadata['file'] = _wp_relative_upload_path( $file );
    92 
    93299                // Make thumbnails and other intermediate sizes.
    94                 $_wp_additional_image_sizes = wp_get_additional_image_sizes();
    95 
    96                 $sizes = array();
    97                 foreach ( get_intermediate_image_sizes() as $s ) {
    98                         $sizes[ $s ] = array(
    99                                 'width'  => '',
    100                                 'height' => '',
    101                                 'crop'   => false,
    102                         );
    103                         if ( isset( $_wp_additional_image_sizes[ $s ]['width'] ) ) {
    104                                 // For theme-added sizes
    105                                 $sizes[ $s ]['width'] = intval( $_wp_additional_image_sizes[ $s ]['width'] );
    106                         } else {
    107                                 // For default sizes set in options
    108                                 $sizes[ $s ]['width'] = get_option( "{$s}_size_w" );
    109                         }
    110 
    111                         if ( isset( $_wp_additional_image_sizes[ $s ]['height'] ) ) {
    112                                 // For theme-added sizes
    113                                 $sizes[ $s ]['height'] = intval( $_wp_additional_image_sizes[ $s ]['height'] );
    114                         } else {
    115                                 // For default sizes set in options
    116                                 $sizes[ $s ]['height'] = get_option( "{$s}_size_h" );
    117                         }
    118 
    119                         if ( isset( $_wp_additional_image_sizes[ $s ]['crop'] ) ) {
    120                                 // For theme-added sizes
    121                                 $sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop'];
    122                         } else {
    123                                 // For default sizes set in options
    124                                 $sizes[ $s ]['crop'] = get_option( "{$s}_crop" );
    125                         }
    126                 }
    127 
    128                 /**
    129                  * Filters the image sizes automatically generated when uploading an image.
    130                  *
    131                  * @since 2.9.0
    132                  * @since 4.4.0 Added the `$metadata` argument.
    133                  * @since 5.1.0 Added the `$attachment_id` argument.
    134                  *
    135                  * @param array $sizes         An associative array of image sizes.
    136                  * @param array $metadata      An associative array of image metadata: width, height, file.
    137                  * @param int   $attachment_id Current attachment ID.
    138                  */
    139                 $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes, $metadata, $attachment_id );
    140 
    141                 if ( $sizes ) {
    142                         $editor = wp_get_image_editor( $file );
    143 
    144                         if ( ! is_wp_error( $editor ) ) {
    145                                 $metadata['sizes'] = $editor->multi_resize( $sizes );
    146                         }
    147                 } else {
    148                         $metadata['sizes'] = array();
    149                 }
    150 
    151                 // Fetch additional metadata from EXIF/IPTC.
    152                 $image_meta = wp_read_image_metadata( $file );
    153                 if ( $image_meta ) {
    154                         $metadata['image_meta'] = $image_meta;
    155                 }
     300                $metadata = wp_create_image_subsizes( $file, $metadata, $attachment_id );
    156301        } elseif ( wp_attachment_is( 'video', $attachment ) ) {
    157302                $metadata = wp_read_video_metadata( $file );
     
    235380                $fallback_sizes = apply_filters( 'fallback_intermediate_image_sizes', $fallback_sizes, $metadata );
    236381
    237                 $sizes                      = array();
    238                 $_wp_additional_image_sizes = wp_get_additional_image_sizes();
    239 
    240                 foreach ( $fallback_sizes as $s ) {
    241                         if ( isset( $_wp_additional_image_sizes[ $s ]['width'] ) ) {
    242                                 $sizes[ $s ]['width'] = intval( $_wp_additional_image_sizes[ $s ]['width'] );
    243                         } else {
    244                                 $sizes[ $s ]['width'] = get_option( "{$s}_size_w" );
    245                         }
    246 
    247                         if ( isset( $_wp_additional_image_sizes[ $s ]['height'] ) ) {
    248                                 $sizes[ $s ]['height'] = intval( $_wp_additional_image_sizes[ $s ]['height'] );
    249                         } else {
    250                                 $sizes[ $s ]['height'] = get_option( "{$s}_size_h" );
    251                         }
    252 
    253                         if ( isset( $_wp_additional_image_sizes[ $s ]['crop'] ) ) {
    254                                 $sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop'];
    255                         } else {
    256                                 // Force thumbnails to be soft crops.
    257                                 if ( 'thumbnail' !== $s ) {
    258                                         $sizes[ $s ]['crop'] = get_option( "{$s}_crop" );
    259                                 }
    260                         }
     382                $defined_sizes = wp_get_registered_image_subsizes();
     383                $merged_sizes  = array_intersect_key( $defined_sizes, array_flip( $fallback_sizes ) );
     384
     385                // Force thumbnails to be soft crops.
     386                if ( isset( $merged_sizes['thumbnail'] ) && is_array( $merged_sizes['thumbnail'] ) ) {
     387                        $merged_sizes['thumbnail']['crop'] = false;
    261388                }
    262389
    263390                // Only load PDFs in an image editor if we're processing sizes.
    264                 if ( ! empty( $sizes ) ) {
     391                if ( ! empty( $merged_sizes ) ) {
    265392                        $editor = wp_get_image_editor( $file );
    266393
     
    283410
    284411                                        if ( ! is_wp_error( $editor ) ) {
    285                                                 $metadata['sizes']         = $editor->multi_resize( $sizes );
     412                                                $metadata['sizes']         = $editor->multi_resize( $merged_sizes );
    286413                                                $metadata['sizes']['full'] = $uploaded;
    287414                                        }
     
    450577        $exif_image_types = apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) );
    451578
    452         if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types ) ) {
     579        if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types, true ) ) {
    453580                $exif = @exif_read_data( $file );
    454581
     
    572699        if ( empty( $info ) ) {
    573700                $result = false;
    574         } elseif ( ! in_array( $info[2], $displayable_image_types ) ) {
     701        } elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) {
    575702                $result = false;
    576703        } else {
     
    655782
    656783        if ( $filepath && file_exists( $filepath ) ) {
    657                 if ( 'full' != $size && ( $data = image_get_intermediate_size( $attachment_id, $size ) ) ) {
     784                $data = image_get_intermediate_size( $attachment_id, $size );
     785
     786                if ( 'full' != $size && $data ) {
     787                        $filepath = path_join( dirname( $filepath ), $data['file'] );
     788
    658789                        /**
    659790                         * Filters the path to the current image.
     
    667798                         * @param string $size          Size of the image.
    668799                         */
    669                         $filepath = apply_filters( 'load_image_to_edit_filesystempath', path_join( dirname( $filepath ), $data['file'] ), $attachment_id, $size );
    670                 }
    671         } elseif ( function_exists( 'fopen' ) && true == ini_get( 'allow_url_fopen' ) ) {
     800                        $filepath = apply_filters( 'load_image_to_edit_filesystempath', $filepath, $attachment_id, $size );
     801                }
     802        } elseif ( function_exists( 'fopen' ) && true === ini_get( 'allow_url_fopen' ) ) {
    672803                /**
    673804                 * Filters the image URL if not in the local filesystem.
     
    706837 */
    707838function _copy_image_file( $attachment_id ) {
    708         $dst_file = $src_file = get_attached_file( $attachment_id );
     839        $dst_file = get_attached_file( $attachment_id );
     840        $src_file = $dst_file;
     841
    709842        if ( ! file_exists( $src_file ) ) {
    710843                $src_file = _load_image_to_edit_path( $attachment_id );
Note: See TracChangeset for help on using the changeset viewer.