Skip to content

Commit f4033b6

Browse files
Ensure secondary mime type filenames are unique
* introduce a $unique parameter that can be set to true in generate_filename, and image editors save, _save, multi_resize & make_subsize functions
1 parent 878965e commit f4033b6

File tree

4 files changed

+63
-32
lines changed

4 files changed

+63
-32
lines changed

src/wp-admin/includes/image.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) {
310310

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

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

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

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

617617
if ( ! empty( $created_sizes ) ) {
618618
foreach ( $created_sizes as $created_size_name => $created_size_meta ) {
@@ -714,20 +714,24 @@ function _wp_make_additional_mime_types( $new_mime_types, $file, $image_meta, $a
714714
continue;
715715
}
716716

717-
$suffix = _wp_get_image_suffix( $resized, $rotated );
717+
$suffix = _wp_get_image_suffix( $resized, $rotated );
718+
$extension = wp_get_default_extension_for_mime_type( $mime_type );
719+
$save_as = $editor->generate_filename( $suffix, null, $extension );
718720

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

725+
$saved = $editor->save( $save_as, $mime_type );
728726
if ( is_wp_error( $saved ) ) {
729727
// TODO: Log errors.
730728
} else {
729+
// If the saved image is larger than the original, discard it.
730+
$filesize = isset( $saved['filesize'] ) ? $saved['filesize'] : wp_filesize( $saved['path'] );
731+
if ( $filesize > $original_file_size ) {
732+
wp_delete_file( $saved['path'] );
733+
continue;
734+
}
731735
$image_meta['sources'][ $mime_type ] = _wp_get_sources_from_meta( $saved );
732736
wp_update_attachment_metadata( $attachment_id, $image_meta );
733737
}

src/wp-includes/class-wp-image-editor-gd.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ protected function _resize( $max_w, $max_h, $crop = false ) {
225225
* each new image is created.
226226
*
227227
* @since 3.5.0
228+
* @since 6.1.0 Added the `$unique` parameter.
228229
*
229230
* @param array $sizes {
230231
* An array of image size data arrays.
@@ -241,13 +242,14 @@ protected function _resize( $max_w, $max_h, $crop = false ) {
241242
* @type bool $crop Optional. Whether to crop the image. Default false.
242243
* }
243244
* }
245+
* @param bool $unique Whether to generate unique file names for the sub-sizes.
244246
* @return array An array of resized images' metadata by size.
245247
*/
246-
public function multi_resize( $sizes ) {
248+
public function multi_resize( $sizes, $unique = false ) {
247249
$metadata = array();
248250

249251
foreach ( $sizes as $size => $size_data ) {
250-
$meta = $this->make_subsize( $size_data );
252+
$meta = $this->make_subsize( $size_data, $unique );
251253

252254
if ( ! is_wp_error( $meta ) ) {
253255
$metadata[ $size ] = $meta;
@@ -261,6 +263,7 @@ public function multi_resize( $sizes ) {
261263
* Create an image sub-size and return the image meta data value for it.
262264
*
263265
* @since 5.3.0
266+
* @since 6.1.0 Added the `$unique` parameter.
264267
*
265268
* @param array $size_data {
266269
* Array of size data.
@@ -269,10 +272,11 @@ public function multi_resize( $sizes ) {
269272
* @type int $height The maximum height in pixels.
270273
* @type bool $crop Whether to crop the image to exact dimensions.
271274
* }
275+
* @param bool $unique Whether to generate unique file names. Default false.
272276
* @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta,
273277
* WP_Error object on error.
274278
*/
275-
public function make_subsize( $size_data ) {
279+
public function make_subsize( $size_data, $unique = false ) {
276280
if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
277281
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
278282
}
@@ -296,7 +300,7 @@ public function make_subsize( $size_data ) {
296300
if ( is_wp_error( $resized ) ) {
297301
$saved = $resized;
298302
} else {
299-
$saved = $this->_save( $resized );
303+
$saved = $this->_save( $resized, null, null, $unique );
300304
imagedestroy( $resized );
301305
}
302306

@@ -426,9 +430,11 @@ public function flip( $horz, $vert ) {
426430
* @since 5.9.0 Renamed `$filename` to `$destfilename` to match parent class
427431
* for PHP 8 named parameter support.
428432
* @since 6.0.0 The `$filesize` value was added to the returned array.
433+
* @since 6.1.0 The `$unique` parameter was added.
429434
*
430435
* @param string|null $destfilename Optional. Destination filename. Default null.
431436
* @param string|null $mime_type Optional. The mime-type. Default null.
437+
* @param bool $unique Whether the filename should be unique. Default false.
432438
* @return array|WP_Error {
433439
* Array on success or WP_Error if the file failed to save.
434440
*
@@ -440,8 +446,8 @@ public function flip( $horz, $vert ) {
440446
* @type int $filesize File size of the image.
441447
* }
442448
*/
443-
public function save( $destfilename = null, $mime_type = null ) {
444-
$saved = $this->_save( $this->image, $destfilename, $mime_type );
449+
public function save( $destfilename = null, $mime_type = null, $unique = false ) {
450+
$saved = $this->_save( $this->image, $destfilename, $mime_type, $unique );
445451

446452
if ( ! is_wp_error( $saved ) ) {
447453
$this->file = $saved['path'];
@@ -454,10 +460,12 @@ public function save( $destfilename = null, $mime_type = null ) {
454460
/**
455461
* @since 3.5.0
456462
* @since 6.0.0 The `$filesize` value was added to the returned array.
463+
* @since 6.1.0 The `$unique` parameter was added.
457464
*
458465
* @param resource|GdImage $image
459466
* @param string|null $filename
460467
* @param string|null $mime_type
468+
* @param bool $unique Whether the filename should be unique. Default false.
461469
* @return array|WP_Error {
462470
* Array on success or WP_Error if the file failed to save.
463471
*
@@ -469,11 +477,15 @@ public function save( $destfilename = null, $mime_type = null ) {
469477
* @type int $filesize File size of the image.
470478
* }
471479
*/
472-
protected function _save( $image, $filename = null, $mime_type = null ) {
480+
protected function _save( $image, $filename = null, $mime_type = null, $unique = false ) {
473481
list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
474482

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

479491
if ( 'image/gif' === $mime_type ) {

src/wp-includes/class-wp-image-editor-imagick.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
436436
* each new image is created.
437437
*
438438
* @since 3.5.0
439+
* @since 6.1.0 Added the `$unique` parameter.
439440
*
440441
* @param array $sizes {
441442
* An array of image size data arrays.
@@ -452,13 +453,14 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
452453
* @type bool $crop Optional. Whether to crop the image. Default false.
453454
* }
454455
* }
456+
* @param bool $unique Whether to generate unique file names for the sub-sizes.
455457
* @return array An array of resized images' metadata by size.
456458
*/
457-
public function multi_resize( $sizes ) {
459+
public function multi_resize( $sizes, $unique = false ) {
458460
$metadata = array();
459461

460462
foreach ( $sizes as $size => $size_data ) {
461-
$meta = $this->make_subsize( $size_data );
463+
$meta = $this->make_subsize( $size_data, $unique );
462464

463465
if ( ! is_wp_error( $meta ) ) {
464466
$metadata[ $size ] = $meta;
@@ -472,18 +474,19 @@ public function multi_resize( $sizes ) {
472474
* Create an image sub-size and return the image meta data value for it.
473475
*
474476
* @since 5.3.0
475-
*
477+
* @since 6.1.0 Added the `$unique` parameter.
476478
* @param array $size_data {
477479
* Array of size data.
478480
*
479481
* @type int $width The maximum width in pixels.
480482
* @type int $height The maximum height in pixels.
481483
* @type bool $crop Whether to crop the image to exact dimensions.
482484
* }
485+
* @param bool $unique Whether to generate a unique file name. Default false.
483486
* @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta,
484487
* WP_Error object on error.
485488
*/
486-
public function make_subsize( $size_data ) {
489+
public function make_subsize( $size_data, $unique = false ) {
487490
if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
488491
return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
489492
}
@@ -508,7 +511,7 @@ public function make_subsize( $size_data ) {
508511
if ( is_wp_error( $resized ) ) {
509512
$saved = $resized;
510513
} else {
511-
$saved = $this->_save( $this->image );
514+
$saved = $this->_save( $this->image, null, null, $unique );
512515

513516
$this->image->clear();
514517
$this->image->destroy();
@@ -662,9 +665,11 @@ public function maybe_exif_rotate() {
662665
*
663666
* @since 3.5.0
664667
* @since 6.0.0 The `$filesize` value was added to the returned array.
668+
* @since 6.1.0 The `$unique` parameter was added.
665669
*
666670
* @param string $destfilename Optional. Destination filename. Default null.
667671
* @param string $mime_type Optional. The mime-type. Default null.
672+
* @param bool $unique Whether the filename should be unique. Default false.
668673
* @return array|WP_Error {
669674
* Array on success or WP_Error if the file failed to save.
670675
*
@@ -676,8 +681,8 @@ public function maybe_exif_rotate() {
676681
* @type int $filesize File size of the image.
677682
* }
678683
*/
679-
public function save( $destfilename = null, $mime_type = null ) {
680-
$saved = $this->_save( $this->image, $destfilename, $mime_type );
684+
public function save( $destfilename = null, $mime_type = null, $unique = false ) {
685+
$saved = $this->_save( $this->image, $destfilename, $mime_type, $unique );
681686

682687
if ( ! is_wp_error( $saved ) ) {
683688
$this->file = $saved['path'];
@@ -696,10 +701,12 @@ public function save( $destfilename = null, $mime_type = null ) {
696701
/**
697702
* @since 3.5.0
698703
* @since 6.0.0 The `$filesize` value was added to the returned array.
704+
* @since 6.1.0 The `$unique` parameter was added.
699705
*
700706
* @param Imagick $image
701707
* @param string $filename
702708
* @param string $mime_type
709+
* @param bool $unique Whether the filename should be unique. Default false.
703710
* @return array|WP_Error {
704711
* Array on success or WP_Error if the file failed to save.
705712
*
@@ -711,11 +718,15 @@ public function save( $destfilename = null, $mime_type = null ) {
711718
* @type int $filesize File size of the image.
712719
* }
713720
*/
714-
protected function _save( $image, $filename = null, $mime_type = null ) {
721+
protected function _save( $image, $filename = null, $mime_type = null, $unique = false ) {
715722
list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
716723

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

721732
try {

src/wp-includes/class-wp-image-editor.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -435,14 +435,15 @@ protected function get_output_format( $filename = null, $mime_type = null ) {
435435
* @since 3.5.0
436436
* @since 6.1.0 Skips adding a suffix when set to an empty string.
437437
*
438-
* @param string $suffix Optional. Suffix to add to the filename. Passing null
439-
* will result in a 'widthxheight' suffix. Passing
440-
* an empty string will result in no suffix.
441-
* @param string $dest_path
442-
* @param string $extension
438+
* @param string $suffix Optional. Suffix to add to the filename. Passing null will result in a 'widthxheight'
439+
* suffix. Passing an empty string will result in no suffix.
440+
* @param string $dest_path The path to the destination folder.
441+
* @param string $extension The file extension to use. By default uses the same extension as the source.
442+
* @param bool $unique Whether to overwrite an existing file. When set to true
443+
* will return a unique file name. Default false.
443444
* @return string filename
444445
*/
445-
public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
446+
public function generate_filename( $suffix = null, $dest_path = null, $extension = null, $unique = false ) {
446447
// $suffix will be appended to the destination filename, just before the extension.
447448
if ( null === $suffix ) {
448449
$suffix = $this->get_suffix();
@@ -471,6 +472,9 @@ public function generate_filename( $suffix = null, $dest_path = null, $extension
471472
$suffix = "-{$suffix}";
472473
}
473474

475+
if ( $unique ) {
476+
return $dir . '/' . wp_unique_filename( $dir, wp_basename( trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}" ) );
477+
}
474478
return trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}";
475479
}
476480

0 commit comments

Comments
 (0)