Skip to content

Commit

Permalink
Ensure secondary mime type filenames are unique
Browse files Browse the repository at this point in the history
* introduce a $unique parameter that can be set to true in generate_filename, and image editors save, _save, multi_resize & make_subsize functions
  • Loading branch information
adamsilverstein committed Jul 12, 2022
1 parent 878965e commit f4033b6
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 32 deletions.
26 changes: 15 additions & 11 deletions src/wp-admin/includes/image.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) {

// Save image only if either it was modified or if the primary mime type is different from the original.
if ( ! empty( $suffix ) || $primary_mime_type !== $imagesize['mime'] ) {
$saved = $editor->save( $editor->generate_filename( $suffix ) );
$saved = $editor->save( $editor->generate_filename( $suffix, null, null ), null, true );

if ( ! is_wp_error( $saved ) ) {
$image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
Expand Down Expand Up @@ -585,7 +585,7 @@ function( $size ) use ( $supported_multi_mime_sizes ) {

if ( method_exists( $editor, 'make_subsize' ) ) {
foreach ( $new_sizes as $new_size_name => $new_size_data ) {
$new_size_meta = $editor->make_subsize( $new_size_data );
$new_size_meta = $editor->make_subsize( $new_size_data, true );

if ( is_wp_error( $new_size_meta ) ) {
// TODO: Log errors.
Expand All @@ -612,7 +612,7 @@ function( $size ) use ( $supported_multi_mime_sizes ) {
}
} else {
// Fall back to `$editor->multi_resize()`.
$created_sizes = $editor->multi_resize( $new_sizes );
$created_sizes = $editor->multi_resize( $new_sizes, true );

if ( ! empty( $created_sizes ) ) {
foreach ( $created_sizes as $created_size_name => $created_size_meta ) {
Expand Down Expand Up @@ -714,20 +714,24 @@ function _wp_make_additional_mime_types( $new_mime_types, $file, $image_meta, $a
continue;
}

$suffix = _wp_get_image_suffix( $resized, $rotated );
$suffix = _wp_get_image_suffix( $resized, $rotated );
$extension = wp_get_default_extension_for_mime_type( $mime_type );
$save_as = $editor->generate_filename( $suffix, null, $extension );

$saved = $editor->save( $editor->generate_filename( $suffix ) );

// If the saved image is larger than the original, discard it.
$filesize = isset( $saved['filesize'] ) ? $saved['filesize'] : wp_filesize( $saved['path'] );
if ( $filesize > $original_file_size ) {
wp_delete_file( $saved['path'] );
continue;
if ( file_exists( $file ) ) {
$save_as = $editor->generate_filename( $suffix, null, $extension, true );
}

$saved = $editor->save( $save_as, $mime_type );
if ( is_wp_error( $saved ) ) {
// TODO: Log errors.
} else {
// If the saved image is larger than the original, discard it.
$filesize = isset( $saved['filesize'] ) ? $saved['filesize'] : wp_filesize( $saved['path'] );
if ( $filesize > $original_file_size ) {
wp_delete_file( $saved['path'] );
continue;
}
$image_meta['sources'][ $mime_type ] = _wp_get_sources_from_meta( $saved );
wp_update_attachment_metadata( $attachment_id, $image_meta );
}
Expand Down
26 changes: 19 additions & 7 deletions src/wp-includes/class-wp-image-editor-gd.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ protected function _resize( $max_w, $max_h, $crop = false ) {
* each new image is created.
*
* @since 3.5.0
* @since 6.1.0 Added the `$unique` parameter.
*
* @param array $sizes {
* An array of image size data arrays.
Expand All @@ -241,13 +242,14 @@ protected function _resize( $max_w, $max_h, $crop = false ) {
* @type bool $crop Optional. Whether to crop the image. Default false.
* }
* }
* @param bool $unique Whether to generate unique file names for the sub-sizes.
* @return array An array of resized images' metadata by size.
*/
public function multi_resize( $sizes ) {
public function multi_resize( $sizes, $unique = false ) {
$metadata = array();

foreach ( $sizes as $size => $size_data ) {
$meta = $this->make_subsize( $size_data );
$meta = $this->make_subsize( $size_data, $unique );

if ( ! is_wp_error( $meta ) ) {
$metadata[ $size ] = $meta;
Expand All @@ -261,6 +263,7 @@ public function multi_resize( $sizes ) {
* Create an image sub-size and return the image meta data value for it.
*
* @since 5.3.0
* @since 6.1.0 Added the `$unique` parameter.
*
* @param array $size_data {
* Array of size data.
Expand All @@ -269,10 +272,11 @@ public function multi_resize( $sizes ) {
* @type int $height The maximum height in pixels.
* @type bool $crop Whether to crop the image to exact dimensions.
* }
* @param bool $unique Whether to generate unique file names. Default false.
* @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta,
* WP_Error object on error.
*/
public function make_subsize( $size_data ) {
public function make_subsize( $size_data, $unique = false ) {
if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
}
Expand All @@ -296,7 +300,7 @@ public function make_subsize( $size_data ) {
if ( is_wp_error( $resized ) ) {
$saved = $resized;
} else {
$saved = $this->_save( $resized );
$saved = $this->_save( $resized, null, null, $unique );
imagedestroy( $resized );
}

Expand Down Expand Up @@ -426,9 +430,11 @@ public function flip( $horz, $vert ) {
* @since 5.9.0 Renamed `$filename` to `$destfilename` to match parent class
* for PHP 8 named parameter support.
* @since 6.0.0 The `$filesize` value was added to the returned array.
* @since 6.1.0 The `$unique` parameter was added.
*
* @param string|null $destfilename Optional. Destination filename. Default null.
* @param string|null $mime_type Optional. The mime-type. Default null.
* @param bool $unique Whether the filename should be unique. Default false.
* @return array|WP_Error {
* Array on success or WP_Error if the file failed to save.
*
Expand All @@ -440,8 +446,8 @@ public function flip( $horz, $vert ) {
* @type int $filesize File size of the image.
* }
*/
public function save( $destfilename = null, $mime_type = null ) {
$saved = $this->_save( $this->image, $destfilename, $mime_type );
public function save( $destfilename = null, $mime_type = null, $unique = false ) {
$saved = $this->_save( $this->image, $destfilename, $mime_type, $unique );

if ( ! is_wp_error( $saved ) ) {
$this->file = $saved['path'];
Expand All @@ -454,10 +460,12 @@ public function save( $destfilename = null, $mime_type = null ) {
/**
* @since 3.5.0
* @since 6.0.0 The `$filesize` value was added to the returned array.
* @since 6.1.0 The `$unique` parameter was added.
*
* @param resource|GdImage $image
* @param string|null $filename
* @param string|null $mime_type
* @param bool $unique Whether the filename should be unique. Default false.
* @return array|WP_Error {
* Array on success or WP_Error if the file failed to save.
*
Expand All @@ -469,11 +477,15 @@ public function save( $destfilename = null, $mime_type = null ) {
* @type int $filesize File size of the image.
* }
*/
protected function _save( $image, $filename = null, $mime_type = null ) {
protected function _save( $image, $filename = null, $mime_type = null, $unique = false ) {
list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );

if ( ! $filename ) {
$filename = $this->generate_filename( null, null, $extension );
// Only generate a unique name when needed. Without this check, a '-#' suffix is added to all sub sizes.
if ( $unique && file_exists( $filename ) ) {
$filename = $this->generate_filename( null, null, $extension, true );
}
}

if ( 'image/gif' === $mime_type ) {
Expand Down
27 changes: 19 additions & 8 deletions src/wp-includes/class-wp-image-editor-imagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
* each new image is created.
*
* @since 3.5.0
* @since 6.1.0 Added the `$unique` parameter.
*
* @param array $sizes {
* An array of image size data arrays.
Expand All @@ -452,13 +453,14 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
* @type bool $crop Optional. Whether to crop the image. Default false.
* }
* }
* @param bool $unique Whether to generate unique file names for the sub-sizes.
* @return array An array of resized images' metadata by size.
*/
public function multi_resize( $sizes ) {
public function multi_resize( $sizes, $unique = false ) {
$metadata = array();

foreach ( $sizes as $size => $size_data ) {
$meta = $this->make_subsize( $size_data );
$meta = $this->make_subsize( $size_data, $unique );

if ( ! is_wp_error( $meta ) ) {
$metadata[ $size ] = $meta;
Expand All @@ -472,18 +474,19 @@ public function multi_resize( $sizes ) {
* Create an image sub-size and return the image meta data value for it.
*
* @since 5.3.0
*
* @since 6.1.0 Added the `$unique` parameter.
* @param array $size_data {
* Array of size data.
*
* @type int $width The maximum width in pixels.
* @type int $height The maximum height in pixels.
* @type bool $crop Whether to crop the image to exact dimensions.
* }
* @param bool $unique Whether to generate a unique file name. Default false.
* @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta,
* WP_Error object on error.
*/
public function make_subsize( $size_data ) {
public function make_subsize( $size_data, $unique = false ) {
if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
}
Expand All @@ -508,7 +511,7 @@ public function make_subsize( $size_data ) {
if ( is_wp_error( $resized ) ) {
$saved = $resized;
} else {
$saved = $this->_save( $this->image );
$saved = $this->_save( $this->image, null, null, $unique );

$this->image->clear();
$this->image->destroy();
Expand Down Expand Up @@ -662,9 +665,11 @@ public function maybe_exif_rotate() {
*
* @since 3.5.0
* @since 6.0.0 The `$filesize` value was added to the returned array.
* @since 6.1.0 The `$unique` parameter was added.
*
* @param string $destfilename Optional. Destination filename. Default null.
* @param string $mime_type Optional. The mime-type. Default null.
* @param bool $unique Whether the filename should be unique. Default false.
* @return array|WP_Error {
* Array on success or WP_Error if the file failed to save.
*
Expand All @@ -676,8 +681,8 @@ public function maybe_exif_rotate() {
* @type int $filesize File size of the image.
* }
*/
public function save( $destfilename = null, $mime_type = null ) {
$saved = $this->_save( $this->image, $destfilename, $mime_type );
public function save( $destfilename = null, $mime_type = null, $unique = false ) {
$saved = $this->_save( $this->image, $destfilename, $mime_type, $unique );

if ( ! is_wp_error( $saved ) ) {
$this->file = $saved['path'];
Expand All @@ -696,10 +701,12 @@ public function save( $destfilename = null, $mime_type = null ) {
/**
* @since 3.5.0
* @since 6.0.0 The `$filesize` value was added to the returned array.
* @since 6.1.0 The `$unique` parameter was added.
*
* @param Imagick $image
* @param string $filename
* @param string $mime_type
* @param bool $unique Whether the filename should be unique. Default false.
* @return array|WP_Error {
* Array on success or WP_Error if the file failed to save.
*
Expand All @@ -711,11 +718,15 @@ public function save( $destfilename = null, $mime_type = null ) {
* @type int $filesize File size of the image.
* }
*/
protected function _save( $image, $filename = null, $mime_type = null ) {
protected function _save( $image, $filename = null, $mime_type = null, $unique = false ) {
list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );

if ( ! $filename ) {
$filename = $this->generate_filename( null, null, $extension );
// Only generate a unique name when needed. Without this check, a '-#' suffix is added to all sub sizes.
if ( $unique && file_exists( $filename ) ) {
$filename = $this->generate_filename( null, null, $extension, true );
}
}

try {
Expand Down
16 changes: 10 additions & 6 deletions src/wp-includes/class-wp-image-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -435,14 +435,15 @@ protected function get_output_format( $filename = null, $mime_type = null ) {
* @since 3.5.0
* @since 6.1.0 Skips adding a suffix when set to an empty string.
*
* @param string $suffix Optional. Suffix to add to the filename. Passing null
* will result in a 'widthxheight' suffix. Passing
* an empty string will result in no suffix.
* @param string $dest_path
* @param string $extension
* @param string $suffix Optional. Suffix to add to the filename. Passing null will result in a 'widthxheight'
* suffix. Passing an empty string will result in no suffix.
* @param string $dest_path The path to the destination folder.
* @param string $extension The file extension to use. By default uses the same extension as the source.
* @param bool $unique Whether to overwrite an existing file. When set to true
* will return a unique file name. Default false.
* @return string filename
*/
public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
public function generate_filename( $suffix = null, $dest_path = null, $extension = null, $unique = false ) {
// $suffix will be appended to the destination filename, just before the extension.
if ( null === $suffix ) {
$suffix = $this->get_suffix();
Expand Down Expand Up @@ -471,6 +472,9 @@ public function generate_filename( $suffix = null, $dest_path = null, $extension
$suffix = "-{$suffix}";
}

if ( $unique ) {
return $dir . '/' . wp_unique_filename( $dir, wp_basename( trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}" ) );
}
return trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}";
}

Expand Down

0 comments on commit f4033b6

Please sign in to comment.