diff --git a/plugins/webp-uploads/hooks.php b/plugins/webp-uploads/hooks.php index 3f5e5014d2..cbe3909c5c 100644 --- a/plugins/webp-uploads/hooks.php +++ b/plugins/webp-uploads/hooks.php @@ -510,74 +510,24 @@ function webp_uploads_remove_sources_files( int $attachment_id ): void { add_action( 'delete_attachment', 'webp_uploads_remove_sources_files', 10, 1 ); /** - * Filters `the_content` to update images so that they use the preferred MIME type where possible. + * Filters `wp_content_img_tag` to update images so that they use the preferred MIME type where possible. * - * By default, this is `image/webp`, if the current attachment contains the targeted MIME - * type. In the near future this will be filterable. + * @since n.e.x.t * - * Note that most of this function will not be needed for an eventual core implementation as it - * would rely on `wp_filter_content_tags()`. - * - * @since 1.0.0 - * - * @see wp_filter_content_tags() - * - * @param string|mixed $content The content of the current post. - * @return string The content with the updated references to the images. + * @param string $filtered_image Full img tag with attributes that will replace the source img tag. + * @param string $context Additional context, like the current filter name or the function name from where this was called. + * @param int $attachment_id The image attachment ID. May be 0 in case the image is not an attachment. + * @return string The updated IMG tag with references to the new MIME type if available. */ -function webp_uploads_update_image_references( $content ): string { - if ( ! is_string( $content ) ) { - $content = ''; - } - +function webp_uploads_filter_image_tag( string $filtered_image, string $context, int $attachment_id ): string { // Bail early if request is not for the frontend. if ( ! webp_uploads_in_frontend_body() ) { - return $content; - } - - // This content does not have any tag on it, move forward. - // TODO: Eventually this should use the HTML API to parse out the image tags and then update them. - if ( 0 === (int) preg_match_all( '/<(img)\s[^>]+>/', $content, $img_tags, PREG_SET_ORDER ) ) { - return $content; - } - - $images = array(); - foreach ( $img_tags as list( $img ) ) { - $processor = new WP_HTML_Tag_Processor( $img ); - if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { - // This condition won't ever be met since we're iterating over the IMG tags extracted with preg_match_all() above. - continue; - } - - // Find the ID of each image by the class. - // TODO: It would be preferable to use the $processor->class_list() method but there seems to be some typing issues with PHPStan. - $class_name = $processor->get_attribute( 'class' ); - if ( - ! is_string( $class_name ) - || - 1 !== preg_match( '/(?:^|\s)wp-image-([1-9]\d*)(?:\s|$)/i', $class_name, $matches ) - ) { - continue; - } - - // Make sure we use the last item on the list of matches. - $images[ $img ] = (int) $matches[1]; + return $filtered_image; } - $attachment_ids = array_unique( array_filter( array_values( $images ) ) ); - if ( count( $attachment_ids ) > 1 ) { - /** - * Warm the object cache with post and meta information for all found - * images to avoid making individual database calls. - */ - _prime_post_caches( $attachment_ids, false, true ); - } + $filtered_image = str_replace( $filtered_image, webp_uploads_img_tag_update_mime_type( $filtered_image, 'the_content', $attachment_id ), $filtered_image ); - foreach ( $images as $img => $attachment_id ) { - $content = str_replace( $img, webp_uploads_img_tag_update_mime_type( $img, 'the_content', $attachment_id ), $content ); - } - - return $content; + return $filtered_image; } /** @@ -773,11 +723,7 @@ function webp_uploads_render_generator(): void { * @since 2.1.0 */ function webp_uploads_init(): void { - if ( webp_uploads_is_picture_element_enabled() ) { - add_filter( 'wp_content_img_tag', 'webp_uploads_wrap_image_in_picture', 10, 3 ); - } else { - add_filter( 'the_content', 'webp_uploads_update_image_references', 13 ); // Run after wp_filter_content_tags. - } + add_filter( 'wp_content_img_tag', webp_uploads_is_picture_element_enabled() ? 'webp_uploads_wrap_image_in_picture' : 'webp_uploads_filter_image_tag', 10, 3 ); } add_action( 'init', 'webp_uploads_init' ); diff --git a/plugins/webp-uploads/tests/data/class-testcase.php b/plugins/webp-uploads/tests/data/class-testcase.php index a8a482ef22..d8176c1cfb 100644 --- a/plugins/webp-uploads/tests/data/class-testcase.php +++ b/plugins/webp-uploads/tests/data/class-testcase.php @@ -138,7 +138,7 @@ public function set_image_output_type( string $format ): void { */ public function opt_in_to_picture_element(): void { remove_filter( 'wp_content_img_tag', 'webp_uploads_wrap_image_in_picture', 10 ); - remove_filter( 'the_content', 'webp_uploads_update_image_references', 13 ); // Run after wp_filter_content_tags. + remove_filter( 'wp_content_img_tag', 'webp_uploads_filter_image_tag', 10 ); // Apply picture element support. update_option( 'webp_uploads_use_picture_element', '1' ); diff --git a/plugins/webp-uploads/tests/test-load.php b/plugins/webp-uploads/tests/test-load.php index 1aa09f5626..24a26fd2be 100644 --- a/plugins/webp-uploads/tests/test-load.php +++ b/plugins/webp-uploads/tests/test-load.php @@ -441,38 +441,38 @@ public function test_it_should_remove_the_backup_sizes_and_sources_if_the_attach /** * Avoid the change of URLs of images that are not part of the media library * - * @covers ::webp_uploads_update_image_references - * @group webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag + * @group webp_uploads_filter_image_tag */ public function test_it_should_avoid_the_change_of_urls_of_images_that_are_not_part_of_the_media_library(): void { // Run critical hooks to satisfy webp_uploads_in_frontend_body() conditions. $this->mock_frontend_body_hooks(); - $paragraph = '

Donec accumsan, sapien et , id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.

'; + $paragraph = ''; - $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) ); + $this->assertSame( $paragraph, webp_uploads_filter_image_tag( $paragraph, 'the_content', 0 ) ); } /** * Avoid replacing not existing attachment IDs * - * @covers ::webp_uploads_update_image_references - * @group webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag + * @group webp_uploads_filter_image_tag */ public function test_it_should_avoid_replacing_not_existing_attachment_i_ds(): void { // Run critical hooks to satisfy webp_uploads_in_frontend_body() conditions. $this->mock_frontend_body_hooks(); - $paragraph = '

Donec accumsan, sapien et , id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.

'; + $paragraph = ''; - $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) ); + $this->assertSame( $paragraph, webp_uploads_filter_image_tag( $paragraph, 'the_content', 0 ) ); } /** * Prevent replacing a WebP image * - * @covers ::webp_uploads_update_image_references - * @group webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag + * @group webp_uploads_filter_image_tag */ public function test_it_should_prevent_replacing_a_webp_image(): void { // Create JPEG and WebP to check that WebP does not get replaced with JPEG. @@ -495,13 +495,13 @@ public function test_it_should_prevent_replacing_a_webp_image(): void { $this->assertNotSame( $tag, $expected_tag ); $this->assertSame( $expected_tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertSame( $expected_tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $expected_tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * Prevent replacing a jpg image if the image does not have the target class name * - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag */ public function test_it_should_prevent_replacing_a_jpg_image_if_the_image_does_not_have_the_target_class_name(): void { $attachment_id = self::factory()->attachment->create_upload_object( @@ -513,16 +513,16 @@ public function test_it_should_prevent_replacing_a_jpg_image_if_the_image_does_n $tag = wp_get_attachment_image( $attachment_id, 'medium' ); - $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * Replace references to a JPG image to a WebP version * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag * @dataProvider provider_replace_images_with_different_extensions - * @group webp_uploads_update_image_references + * @group webp_uploads_filter_image_tag */ public function test_it_should_replace_references_to_a_jpg_image_to_a_webp_version( string $image_path ): void { // Create JPEG and WebP to check replacement of JPEG => WebP. @@ -543,16 +543,16 @@ public function test_it_should_replace_references_to_a_jpg_image_to_a_webp_versi $this->assertNotSame( $tag, $expected_tag ); $this->assertSame( $expected_tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertSame( $expected_tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $expected_tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * Should not replace jpeg images in the content if other mime types are disabled via filter. * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag * @dataProvider provider_replace_images_with_different_extensions - * @group webp_uploads_update_image_references + * @group webp_uploads_filter_image_tag */ public function test_it_should_not_replace_the_references_to_a_jpg_image_when_disabled_via_filter( string $image_path ): void { remove_all_filters( 'webp_uploads_content_image_mimes' ); @@ -570,7 +570,7 @@ static function ( $mime_types ) { $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } public function provider_replace_images_with_different_extensions(): Generator { @@ -582,7 +582,7 @@ public function provider_replace_images_with_different_extensions(): Generator { * Replace all the images including the full size image * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag */ public function test_it_should_replace_all_the_images_including_the_full_size_image(): void { // Create JPEG and WebP to check replacement of JPEG => WebP. @@ -602,17 +602,17 @@ public function test_it_should_replace_all_the_images_including_the_full_size_im $this->assertSame( $expected, wp_check_filetype( get_attached_file( $attachment_id ) ) ); $this->mock_frontend_body_hooks(); $this->assertStringNotContainsString( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - $this->assertStringNotContainsString( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_update_image_references( $tag ) ); + $this->assertStringNotContainsString( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); $this->assertStringContainsString( $metadata['sources']['image/webp']['file'], webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - $this->assertStringNotContainsString( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_update_image_references( $tag ) ); + $this->assertStringNotContainsString( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * Prevent replacing an image with no available sources * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references - * @group webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag + * @group webp_uploads_filter_image_tag */ public function test_it_should_prevent_replacing_an_image_with_no_available_sources(): void { add_filter( 'webp_uploads_upload_image_mime_transforms', '__return_empty_array' ); @@ -622,16 +622,16 @@ public function test_it_should_prevent_replacing_an_image_with_no_available_sour $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * Prevent update not supported images with no available sources * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag * @dataProvider data_provider_not_supported_webp_images - * @group webp_uploads_update_image_references + * @group webp_uploads_filter_image_tag */ public function test_it_should_prevent_update_not_supported_images_with_no_available_sources( string $image_path ): void { $attachment_id = self::factory()->attachment->create_upload_object( $image_path ); @@ -641,7 +641,7 @@ public function test_it_should_prevent_update_not_supported_images_with_no_avail $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } public function data_provider_not_supported_webp_images(): Generator { @@ -800,8 +800,8 @@ public function data_provider_supported_image_types_with_threshold(): array { /** * Prevent replacing an image if image was uploaded via external source or plugin. * - * @covers ::webp_uploads_update_image_references - * @group webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag + * @group webp_uploads_filter_image_tag */ public function test_it_should_prevent_replacing_an_image_uploaded_via_external_source(): void { remove_all_filters( 'webp_uploads_pre_replace_additional_image_source' ); @@ -818,14 +818,14 @@ static function () { $tag = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) ); $this->assertNotSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertNotSame( $tag, webp_uploads_update_image_references( $tag ) ); + $this->assertNotSame( $tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * The image with the smaller filesize should be used when webp_uploads_discard_larger_generated_images is set to true. * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag */ public function test_it_should_create_webp_when_webp_is_smaller_than_jpegs(): void { // Create JPEG and WebP. @@ -844,7 +844,7 @@ public function test_it_should_create_webp_when_webp_is_smaller_than_jpegs(): vo $this->assertImageHasSource( $attachment_id, 'image/webp' ); $this->assertImageHasSizeSource( $attachment_id, 'thumbnail', 'image/webp' ); $this->mock_frontend_body_hooks(); - $this->assertSame( $result, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $result, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); $this->assertNotSame( $tag, $result ); @@ -867,7 +867,7 @@ public function test_it_should_create_webp_when_webp_is_smaller_than_jpegs(): vo * The image with the smaller filesize should be used when webp_uploads_discard_larger_generated_images is set to true. * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag */ public function test_it_should_create_webp_for_full_size_which_is_smaller_in_webp_format(): void { // Create JPEG and WebP. @@ -890,14 +890,14 @@ public function test_it_should_create_webp_for_full_size_which_is_smaller_in_web } $this->assertNotSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); $this->mock_frontend_body_hooks(); - $this->assertNotSame( $tag, webp_uploads_update_image_references( $tag ) ); + $this->assertNotSame( $tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** * The image with the smaller filesize should be used when webp_uploads_discard_larger_generated_images is set to true. * * @covers ::webp_uploads_img_tag_update_mime_type - * @covers ::webp_uploads_update_image_references + * @covers ::webp_uploads_filter_image_tag */ public function test_it_should_create_webp_for_some_sizes_which_are_smaller_in_webp_format(): void { // Create JPEG and WebP. @@ -926,7 +926,7 @@ public function test_it_should_create_webp_for_some_sizes_which_are_smaller_in_w $this->assertNotSame( $tag, $updated_tag ); $this->assertSame( $expected_tag, $updated_tag ); $this->mock_frontend_body_hooks(); - $this->assertSame( $expected_tag, webp_uploads_update_image_references( $tag ) ); + $this->assertSame( $expected_tag, webp_uploads_filter_image_tag( $tag, 'the_content', $attachment_id ) ); } /** diff --git a/plugins/webp-uploads/tests/test-picture-element.php b/plugins/webp-uploads/tests/test-picture-element.php index dd2d7c7d78..6618f0dabe 100644 --- a/plugins/webp-uploads/tests/test-picture-element.php +++ b/plugins/webp-uploads/tests/test-picture-element.php @@ -119,9 +119,10 @@ public function test_maybe_wrap_images_in_picture_element( bool $fallback_jpeg, $expected_html = str_replace( array_keys( $replacements ), array_values( $replacements ), $expected_html ); - // Apply the wp_content_img_tag filter. - $image = apply_filters( 'wp_content_img_tag', $image, 'the_content', self::$image_id ); - + if ( webp_uploads_is_picture_element_enabled() ) { + // Apply the wp_content_img_tag filter. + $image = apply_filters( 'wp_content_img_tag', $image, 'the_content', self::$image_id ); + } // Check that the image has the expected HTML. $this->assertEquals( $expected_html, $image ); }