Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions plugins/webp-uploads/hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,30 @@ function webp_uploads_create_sources_property( array $metadata, int $attachment_
return $metadata;
}

/*
* If the base image metadata is missing its dimensions, the core sub-size
* generation failed at upload time. This happens when wp_create_image_subsizes()
* bails early because wp_getimagesize() could not read the file -- typically an
* intermittent race where the upload is read before it has finished being written
* to disk. The result is metadata without `width`/`height`/`sizes`, which renders
* the attachment at 1x1 pixels both in the Media Library and on the front end.
*
* The file is readable by the time this filter runs, so attempt to recover the
* original dimensions from it. If they can be recovered, the full-size image will
* display correctly again (responsive sub-sizes still require regeneration). If the
* file still cannot be read, bail without decorating or persisting a partial record.
*
* See https://github.com/WordPress/performance/issues/2468.
*/
if ( ! isset( $metadata['width'], $metadata['height'] ) || (int) $metadata['width'] < 1 || (int) $metadata['height'] < 1 ) {
$image_size = wp_getimagesize( $file );
if ( ! is_array( $image_size ) ) {
return $metadata;
}
$metadata['width'] = $image_size[0];
$metadata['height'] = $image_size[1];
}

// Make sure the top level `sources` key is a valid array.
if ( ! isset( $metadata['sources'] ) || ! is_array( $metadata['sources'] ) ) {
$metadata['sources'] = array();
Expand Down
61 changes: 61 additions & 0 deletions plugins/webp-uploads/tests/test-load.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,67 @@ public function test_it_should_create_the_original_mime_type_as_well_with_all_th
}
}

/**
* Recover the original image dimensions when the base metadata is incomplete.
*
* When core's wp_create_image_subsizes() cannot read the uploaded file (an
* intermittent race at upload time) it returns metadata without width/height/sizes,
* which makes the attachment render at 1x1 pixels. The filter should recover the
* dimensions from the now-readable file instead of decorating a 1x1 record.
*
* @link https://github.com/WordPress/performance/issues/2468
*
* @covers ::webp_uploads_create_sources_property
*/
public function test_it_should_recover_dimensions_when_base_metadata_is_incomplete(): void {
$attachment_id = self::factory()->attachment->create_upload_object( TESTS_PLUGIN_DIR . '/tests/data/images/leaves.jpg' );

// The metadata generated during upload holds the real dimensions.
$generated = wp_get_attachment_metadata( $attachment_id );
$this->assertArrayHasKey( 'width', $generated );
$this->assertArrayHasKey( 'height', $generated );
$this->assertGreaterThan( 1, $generated['width'] );
$this->assertGreaterThan( 1, $generated['height'] );

// Simulate the core failure: base metadata without dimensions or sizes.
$result = webp_uploads_create_sources_property( array(), $attachment_id );

// The dimensions should be recovered from the readable file, not left empty.
$this->assertArrayHasKey( 'width', $result );
$this->assertArrayHasKey( 'height', $result );
$this->assertSame( $generated['width'], $result['width'] );
$this->assertSame( $generated['height'], $result['height'] );

// Sources are attached to the now-valid record rather than to a 1x1 placeholder.
$this->assertArrayHasKey( 'sources', $result );
}

/**
* Do not decorate or persist metadata when the file cannot be read.
*
* If the dimensions still cannot be recovered (the file is genuinely unreadable),
* the filter must bail without inventing dimensions or adding a sources property.
*
* @link https://github.com/WordPress/performance/issues/2468
*
* @covers ::webp_uploads_create_sources_property
*/
public function test_it_should_not_decorate_metadata_when_file_is_missing(): void {
$attachment_id = self::factory()->attachment->create_upload_object( TESTS_PLUGIN_DIR . '/tests/data/images/leaves.jpg' );

// Remove the underlying file so it can no longer be read.
$file = get_attached_file( $attachment_id );
$this->assertIsString( $file );
wp_delete_file( $file );

$result = webp_uploads_create_sources_property( array(), $attachment_id );

// No dimensions invented and no sources attached to the empty record.
$this->assertArrayNotHasKey( 'width', $result );
$this->assertArrayNotHasKey( 'height', $result );
$this->assertArrayNotHasKey( 'sources', $result );
}

/**
* Create JPEG and output format for JPEG images, if perflab_generate_webp_and_jpeg option set.
*
Expand Down