Changeset 63002
- Timestamp:
- 08/04/2026 01:56:04 AM (3 weeks ago)
- Location:
- trunk
- Files:
-
- 1 added
- 5 edited
-
src/wp-admin/includes/image-edit.php (modified) (2 diffs)
-
src/wp-includes/media.php (modified) (1 diff)
-
src/wp-includes/post.php (modified) (3 diffs)
-
tests/phpunit/tests/ajax/wpAjaxImageEditor.php (modified) (1 diff)
-
tests/phpunit/tests/media.php (modified) (2 diffs)
-
tests/phpunit/tests/post/wpGetAttachmentMetadata.php (added)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/includes/image-edit.php
r62840 r63002 821 821 $msg = new stdClass(); 822 822 823 if ( ! is_array( $ backup_sizes ) ) {823 if ( ! is_array( $meta ) || ! is_array( $backup_sizes ) ) { 824 824 $msg->error = __( 'Cannot load image metadata.' ); 825 825 return $msg; 826 826 } 827 828 $meta['sizes'] ??= array(); 827 829 828 830 $parts = pathinfo( $file ); … … 983 985 return $return; 984 986 } 987 988 $meta['sizes'] ??= array(); 985 989 986 990 if ( ! is_array( $backup_sizes ) ) { -
trunk/src/wp-includes/media.php
r62978 r63002 4815 4815 4816 4816 $response = array_merge( $response, $sizes['full'] ); 4817 } elseif ( $meta['sizes']['full']['file'] ) { 4817 } elseif ( 4818 ! empty( $meta['sizes']['full']['file'] ) && 4819 isset( $meta['sizes']['full']['width'], $meta['sizes']['full']['height'] ) 4820 ) { 4818 4821 $sizes['full'] = array( 4819 4822 'url' => esc_url_raw( $base_url . $meta['sizes']['full']['file'] ), -
trunk/src/wp-includes/post.php
r62845 r63002 7049 7049 * @since 2.1.0 7050 7050 * @since 6.0.0 The `$filesize` value was added to the returned array. 7051 * @since 7.1.0 `false` is now returned if the metadata is not an array, and when the result is 7052 * filtered the `sizes` key is always an array when present. 7051 7053 * 7052 7054 * @param int $attachment_id Attachment post ID. Defaults to global $post. … … 7112 7114 $data = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); 7113 7115 7114 if ( ! $data ) {7116 if ( ! is_array( $data ) || ! $data ) { 7115 7117 return false; 7116 7118 } … … 7128 7130 * @param int $attachment_id Attachment post ID. 7129 7131 */ 7130 return apply_filters( 'wp_get_attachment_metadata', $data, $attachment_id ); 7132 $data = apply_filters( 'wp_get_attachment_metadata', $data, $attachment_id ); 7133 7134 if ( ! is_array( $data ) ) { 7135 return false; 7136 } 7137 7138 if ( array_key_exists( 'sizes', $data ) && ! is_array( $data['sizes'] ) ) { 7139 $data['sizes'] = array(); 7140 } 7141 7142 return $data; 7131 7143 } 7132 7144 -
trunk/tests/phpunit/tests/ajax/wpAjaxImageEditor.php
r59202 r63002 195 195 $this->assertSameSetsWithIndex( $pre_file_sizes, $post_restore_file_sizes, 'Filesize should have restored after restoring the original image.' ); 196 196 } 197 198 /** 199 * Ensure editing an image does not fatal when the attachment metadata has no usable `sizes` data. 200 * 201 * Attachment metadata is not guaranteed to contain a `sizes` array. It can be missing when 202 * sub-size generation never ran or failed (for example `wp_create_image_subsizes()` returns an 203 * empty array when the file cannot be parsed), or when it is removed by a plugin filtering 204 * `wp_get_attachment_metadata`. `wp_save_image()` only validates that the metadata itself is an 205 * array, then passes `$meta['sizes']` straight to `array_merge()`. 206 * 207 * @ticket 65748 208 * 209 * @covers ::wp_save_image 210 * 211 * @dataProvider data_save_image_with_unusable_sizes_metadata 212 * 213 * @param array{ sizes?: mixed } $meta Attachment metadata to store before editing, minus the file-specific keys. 214 */ 215 public function test_save_image_with_unusable_sizes_metadata( array $meta ) { 216 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 217 218 $filename = DIR_TESTDATA . '/images/canola.jpg'; 219 $contents = file_get_contents( $filename ); 220 $this->assertIsString( $contents ); 221 222 $upload = wp_upload_bits( wp_basename( $filename ), null, $contents ); 223 $id = $this->_make_attachment( $upload ); 224 $this->assertIsInt( $id ); 225 226 $original_meta = wp_get_attachment_metadata( $id ); 227 $this->assertIsArray( $original_meta ); 228 229 // Keep the real file/dimension data, only make `sizes` unusable. 230 $meta = array_merge( 231 wp_array_slice_assoc( $original_meta, array( 'width', 'height', 'file', 'filesize' ) ), 232 $meta 233 ); 234 235 wp_update_attachment_metadata( $id, $meta ); 236 237 $_REQUEST['action'] = 'image-editor'; 238 $_REQUEST['context'] = 'edit-attachment'; 239 $_REQUEST['postid'] = $id; 240 $_REQUEST['target'] = 'all'; 241 $_REQUEST['do'] = 'save'; 242 $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":289,"h":322}}]'; 243 244 $ret = wp_save_image( $id ); 245 246 $this->assertObjectNotHasProperty( 'error', $ret, 'Saving the image should not have returned an error.' ); 247 248 $saved_meta = wp_get_attachment_metadata( $id ); 249 250 $this->assertIsArray( $saved_meta, 'The saved attachment metadata should be an array.' ); 251 $this->assertArrayHasKey( 'sizes', $saved_meta ); 252 $this->assertIsArray( $saved_meta['sizes'], 'The saved attachment metadata should contain a `sizes` array.' ); 253 $this->assertArrayHasKey( 'thumbnail', $saved_meta['sizes'], 'The edited image should have regenerated the thumbnail size.' ); 254 } 255 256 /** 257 * Ensure restoring an image does not fatal when the attachment metadata has no usable `sizes` data. 258 * 259 * `wp_restore_image()` writes each backed up size with `$meta['sizes'][ $default_size ] = $data` 260 * without ever checking that `$meta['sizes']` is an array. A scalar value raises 261 * "Cannot use a scalar value as an array", and `false` is deprecated as of PHP 8.1 and 262 * an error as of PHP 9. The same metadata that fatals `wp_save_image()` reaches this code. 263 * 264 * @ticket 65748 265 * 266 * @covers ::wp_restore_image 267 * 268 * @dataProvider data_save_image_with_unusable_sizes_metadata 269 * 270 * @param array{ sizes?: mixed } $meta Replacement `sizes` metadata to store before restoring. 271 */ 272 public function test_restore_image_with_unusable_sizes_metadata( array $meta ) { 273 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 274 275 $filename = DIR_TESTDATA . '/images/canola.jpg'; 276 $contents = file_get_contents( $filename ); 277 $this->assertIsString( $contents ); 278 279 $upload = wp_upload_bits( wp_basename( $filename ), null, $contents ); 280 $id = $this->_make_attachment( $upload ); 281 $this->assertIsInt( $id ); 282 283 $_REQUEST['action'] = 'image-editor'; 284 $_REQUEST['context'] = 'edit-attachment'; 285 $_REQUEST['postid'] = $id; 286 $_REQUEST['target'] = 'all'; 287 $_REQUEST['do'] = 'save'; 288 $_REQUEST['history'] = '[{"c":{"x":5,"y":8,"w":289,"h":322}}]'; 289 290 // Edit the image first so that `_wp_attachment_backup_sizes` holds the original sizes. 291 wp_save_image( $id ); 292 293 $this->assertNotEmpty( 294 get_post_meta( $id, '_wp_attachment_backup_sizes', true ), 295 'The image edit should have stored backup sizes to restore from.' 296 ); 297 298 // Keep the metadata written by the edit, only make `sizes` unusable. 299 $edited_meta = wp_get_attachment_metadata( $id ); 300 $this->assertIsArray( $edited_meta ); 301 unset( $edited_meta['sizes'] ); 302 303 wp_update_attachment_metadata( $id, array_merge( $edited_meta, $meta ) ); 304 305 wp_restore_image( $id ); 306 307 $restored_meta = wp_get_attachment_metadata( $id ); 308 $this->assertIsArray( $restored_meta ); 309 310 $this->assertArrayHasKey( 'sizes', $restored_meta ); 311 $this->assertIsArray( $restored_meta['sizes'], 'The restored attachment metadata should contain a `sizes` array.' ); 312 $this->assertArrayHasKey( 'thumbnail', $restored_meta['sizes'], 'The restored image should have the thumbnail size restored from the backup sizes.' ); 313 } 314 315 /** 316 * Data provider. 317 * 318 * @return array<non-empty-string, array{ 0: array{ sizes?: mixed } }> 319 */ 320 public function data_save_image_with_unusable_sizes_metadata(): array { 321 return array( 322 'no sizes key' => array( array() ), 323 'null sizes' => array( array( 'sizes' => null ) ), 324 'empty string' => array( array( 'sizes' => '' ) ), 325 'string sizes' => array( array( 'sizes' => 'not-an-array' ) ), 326 'boolean sizes' => array( array( 'sizes' => false ) ), 327 ); 328 } 197 329 } -
trunk/tests/phpunit/tests/media.php
r62978 r63002 666 666 667 667 $this->assertArrayHasKey( 'sizes', $prepped ); 668 } 669 670 /** 671 * Tests that an unusable `full` entry in the `sizes` metadata is skipped. 672 * 673 * Attachments that are not images, such as PDFs, are handled by a separate branch that reads 674 * the `full` entry of the `sizes` metadata directly. That entry is not guaranteed to be there, 675 * nor to carry dimensions when it is, and reading it unconditionally raises "Undefined array 676 * key" warnings. 677 * 678 * @ticket 65748 679 * 680 * @dataProvider data_wp_prepare_attachment_for_js_unusable_full_size 681 * 682 * @covers ::wp_prepare_attachment_for_js 683 * 684 * @param array<string, mixed> $sizes Value to store as the `sizes` metadata. 685 */ 686 public function test_wp_prepare_attachment_for_js_with_an_unusable_full_size( array $sizes ) { 687 $id = $this->create_pdf_attachment( $sizes ); 688 689 $prepped = wp_prepare_attachment_for_js( $id ); 690 691 $this->assertIsArray( $prepped ); 692 $this->assertArrayHasKey( 'sizes', $prepped ); 693 694 $sizes = $prepped['sizes']; 695 696 $this->assertIsArray( $sizes ); 697 $this->assertArrayNotHasKey( 'full', $sizes, 'An unusable `full` size should not have been exposed.' ); 698 } 699 700 /** 701 * Tests that a usable `full` entry in the `sizes` metadata is still exposed. 702 * 703 * @ticket 65748 704 * 705 * @covers ::wp_prepare_attachment_for_js 706 */ 707 public function test_wp_prepare_attachment_for_js_with_a_usable_full_size() { 708 $id = $this->create_pdf_attachment( 709 array( 710 'full' => array( 711 'file' => 'test-document-pdf.jpg', 712 'width' => 232, 713 'height' => 300, 714 'mime-type' => 'image/jpeg', 715 ), 716 ) 717 ); 718 719 $prepped = wp_prepare_attachment_for_js( $id ); 720 721 $this->assertIsArray( $prepped ); 722 $this->assertArrayHasKey( 'sizes', $prepped ); 723 724 $sizes = $prepped['sizes']; 725 726 $this->assertIsArray( $sizes ); 727 $this->assertArrayHasKey( 'full', $sizes, 'A usable `full` size should have been exposed.' ); 728 729 $full = $sizes['full']; 730 731 $this->assertIsArray( $full ); 732 $this->assertSame( 232, $full['width'] ); 733 $this->assertSame( 300, $full['height'] ); 734 $this->assertSame( 'portrait', $full['orientation'] ); 735 $this->assertIsString( $full['url'] ); 736 $this->assertStringEndsWith( '/test-document-pdf.jpg', $full['url'] ); 737 } 738 739 /** 740 * Data provider. 741 * 742 * @return array<non-empty-string, array{ 0: array<string, mixed> }> 743 */ 744 public function data_wp_prepare_attachment_for_js_unusable_full_size(): array { 745 return array( 746 'no full size' => array( 747 array( 748 'thumbnail' => array( 749 'file' => 'test-document-pdf-116x150.jpg', 750 'width' => 116, 751 'height' => 150, 752 'mime-type' => 'image/jpeg', 753 ), 754 ), 755 ), 756 'full without dimensions' => array( 757 array( 758 'full' => array( 759 'file' => 'test-document-pdf.jpg', 760 'mime-type' => 'image/jpeg', 761 ), 762 ), 763 ), 764 'full without a height' => array( 765 array( 766 'full' => array( 767 'file' => 'test-document-pdf.jpg', 768 'width' => 232, 769 'mime-type' => 'image/jpeg', 770 ), 771 ), 772 ), 773 'full with an empty file' => array( 774 array( 775 'full' => array( 776 'file' => '', 777 'width' => 232, 778 'height' => 300, 779 'mime-type' => 'image/jpeg', 780 ), 781 ), 782 ), 783 ); 784 } 785 786 /** 787 * Creates a PDF attachment carrying the given `sizes` metadata. 788 * 789 * A PDF is used so that wp_prepare_attachment_for_js() takes the branch for attachments that 790 * are not images, which is the one that reads the `full` entry of the `sizes` metadata. 791 * 792 * @param array<string, mixed> $sizes Value to store as the `sizes` metadata. 793 * @return int Attachment ID. 794 */ 795 private function create_pdf_attachment( array $sizes ): int { 796 $id = wp_insert_attachment( 797 array( 798 'post_title' => 'Attachment Title', 799 'post_type' => 'attachment', 800 'post_parent' => 0, 801 'post_mime_type' => 'application/pdf', 802 'guid' => home_url( '/wp-content/uploads/test-document.pdf' ), 803 ) 804 ); 805 806 wp_update_attachment_metadata( 807 $id, 808 array( 809 'file' => 'test-document.pdf', 810 'sizes' => $sizes, 811 ) 812 ); 813 814 return $id; 668 815 } 669 816 … … 3179 3326 * @ticket 36246 3180 3327 * @requires function imagejpeg 3328 * 3329 * @covers ::wp_get_attachment_image 3330 * @covers ::wp_get_attachment_metadata 3181 3331 */ 3182 3332 public function test_wp_get_attachment_image_should_use_wp_get_attachment_metadata() {
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)