From a556ae174b8bb8081180cdb810befecdb33512ca Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Fri, 22 May 2026 01:15:31 -0700 Subject: [PATCH 01/12] fix: ensure figcaptions have unique IDs --- src/wp-includes/media.php | 2 ++ tests/phpunit/tests/media.php | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 8f6ec1cef4e26..fac7fa91df0ff 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2610,6 +2610,8 @@ function img_caption_shortcode( $attr, $content = '' ) { } if ( $atts['caption_id'] ) { + // Add random component to figcaption ID to ensure uniqueness when same image appears multiple times on page. + $atts['caption_id'] .= '-' . substr( md5( microtime( true ) . wp_rand() ), 0, 2 ); $caption_id = 'id="' . esc_attr( $atts['caption_id'] ) . '" '; $describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] ) . '" '; } diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 060a7295f6bb4..56884c271d3c3 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -305,6 +305,60 @@ public function test_img_caption_shortcode_has_aria_describedby() { $this->assertSame( 1, substr_count( $result, 'aria-describedby="caption-myId"' ) ); } + /** + * Test that figcaption IDs are unique on the page, even if same image and caption appear multiple times. + */ + public function test_img_caption_shortcode_unique_ids_per_instance() { + // First instance with caption "My caption" + $result_1 = img_caption_shortcode( + array( + 'width' => 20, + 'id' => 'attachment_123', + 'caption' => 'My caption', + ), + self::IMG_CONTENT . 'My caption' + ); + + // Second instance - identical to first + $result_2 = img_caption_shortcode( + array( + 'width' => 20, + 'id' => 'attachment_123', + 'caption' => 'My caption', + ), + self::IMG_CONTENT . 'My caption' + ); + + // Third instance - same image, different caption + $result_3 = img_caption_shortcode( + array( + 'width' => 20, + 'id' => 'attachment_123', + 'caption' => 'Different caption', + ), + self::IMG_CONTENT . 'Different caption' + ); + + // Extract figcaption IDs from results + preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_1, $matches_1 ); + preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_2, $matches_2 ); + preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_3, $matches_3 ); + + $caption_id_1 = isset( $matches_1[1] ) ? $matches_1[1] : ''; + $caption_id_2 = isset( $matches_2[1] ) ? $matches_2[1] : ''; + $caption_id_3 = isset( $matches_3[1] ) ? $matches_3[1] : ''; + + // All should exist + $this->assertNotEmpty( $caption_id_1, 'First caption should have an ID' ); + $this->assertNotEmpty( $caption_id_2, 'Second caption should have an ID' ); + $this->assertNotEmpty( $caption_id_3, 'Third caption should have an ID' ); + + // All should be different (each instance gets unique ID) + $this->assertNotSame( $caption_id_1, $caption_id_2, 'First and second captions should have different IDs even with identical content' ); + $this->assertNotSame( $caption_id_2, $caption_id_3, 'Second and third captions should have different IDs' ); + $this->assertNotSame( $caption_id_1, $caption_id_3, 'First and third captions should have different IDs' ); + } + public function test_add_remove_oembed_provider() { wp_oembed_add_provider( 'http://foo.bar/*', 'http://foo.bar/oembed' ); $this->assertTrue( wp_oembed_remove_provider( 'http://foo.bar/*' ) ); From be000e73642bd56188e8b34f6209ea262784d5e1 Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Fri, 22 May 2026 01:22:30 -0700 Subject: [PATCH 02/12] chore: fix linting --- src/wp-includes/media.php | 7 ++++--- tests/phpunit/tests/media.php | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index fac7fa91df0ff..2737c7383f432 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2610,10 +2610,11 @@ function img_caption_shortcode( $attr, $content = '' ) { } if ( $atts['caption_id'] ) { - // Add random component to figcaption ID to ensure uniqueness when same image appears multiple times on page. + // Add random component to figcaption ID to ensure uniqueness when + // same image appears multiple times on page. $atts['caption_id'] .= '-' . substr( md5( microtime( true ) . wp_rand() ), 0, 2 ); - $caption_id = 'id="' . esc_attr( $atts['caption_id'] ) . '" '; - $describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] ) . '" '; + $caption_id = 'id="' . esc_attr( $atts['caption_id'] ) . '" '; + $describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] ) . '" '; } $class = trim( 'wp-caption ' . $atts['align'] . ' ' . $atts['class'] ); diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 56884c271d3c3..5b56877c6aebf 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -306,7 +306,13 @@ public function test_img_caption_shortcode_has_aria_describedby() { } /** - * Test that figcaption IDs are unique on the page, even if same image and caption appear multiple times. + * Tests that figcaption IDs are unique for multiple caption instances. + * + * When the same image with the same or different captions appears multiple + * times on a page, each figcaption should receive a unique ID to maintain + * HTML validity and accessibility. + * + * @since 7.1.0 */ public function test_img_caption_shortcode_unique_ids_per_instance() { // First instance with caption "My caption" From fb96413b56dec6b256c421395b5827ac1311e17f Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Fri, 22 May 2026 01:35:19 -0700 Subject: [PATCH 03/12] fix: generate unique ID for figure elements --- src/wp-includes/media.php | 14 ++++++------ tests/phpunit/tests/media.php | 42 +++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 2737c7383f432..31832d6d53801 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2599,8 +2599,11 @@ function img_caption_shortcode( $attr, $content = '' ) { $describedby = ''; if ( $atts['id'] ) { - $atts['id'] = sanitize_html_class( $atts['id'] ); - $id = 'id="' . esc_attr( $atts['id'] ) . '" '; + // Add random component to figure ID to ensure uniqueness when + // same image appears multiple times on page. + $caption_id_random = '-' . substr( md5( microtime( true ) . wp_rand() ), 0, 2 ); + $atts['id'] = sanitize_html_class( $atts['id'] ); + $id = 'id="' . esc_attr( $atts['id'] . $caption_id_random ) . '" '; } if ( $atts['caption_id'] ) { @@ -2610,11 +2613,8 @@ function img_caption_shortcode( $attr, $content = '' ) { } if ( $atts['caption_id'] ) { - // Add random component to figcaption ID to ensure uniqueness when - // same image appears multiple times on page. - $atts['caption_id'] .= '-' . substr( md5( microtime( true ) . wp_rand() ), 0, 2 ); - $caption_id = 'id="' . esc_attr( $atts['caption_id'] ) . '" '; - $describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] ) . '" '; + $caption_id = 'id="' . esc_attr( $atts['caption_id'] . $caption_id_random ) . '" '; + $describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] . $caption_id_random ) . '" '; } $class = trim( 'wp-caption ' . $atts['align'] . ' ' . $atts['class'] ); diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 5b56877c6aebf..5429b56746a45 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -306,11 +306,11 @@ public function test_img_caption_shortcode_has_aria_describedby() { } /** - * Tests that figcaption IDs are unique for multiple caption instances. + * Tests that both figure and figcaption IDs are unique for multiple caption instances. * * When the same image with the same or different captions appears multiple - * times on a page, each figcaption should receive a unique ID to maintain - * HTML validity and accessibility. + * times on a page, each figure and figcaption should receive a unique ID to + * maintain HTML validity and accessibility. * * @since 7.1.0 */ @@ -345,21 +345,39 @@ public function test_img_caption_shortcode_unique_ids_per_instance() { self::IMG_CONTENT . 'Different caption' ); - // Extract figcaption IDs from results - preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_1, $matches_1 ); - preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_2, $matches_2 ); - preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_3, $matches_3 ); + // Extract figure IDs from results (format: id="attachment_123-XX") + preg_match( '/id="(attachment_123[^"]*)"/', $result_1, $fig_matches_1 ); + preg_match( '/id="(attachment_123[^"]*)"/', $result_2, $fig_matches_2 ); + preg_match( '/id="(attachment_123[^"]*)"/', $result_3, $fig_matches_3 ); - $caption_id_1 = isset( $matches_1[1] ) ? $matches_1[1] : ''; - $caption_id_2 = isset( $matches_2[1] ) ? $matches_2[1] : ''; - $caption_id_3 = isset( $matches_3[1] ) ? $matches_3[1] : ''; + // Extract figcaption IDs from results (format: id="caption-attachment-123-XX") + preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_1, $cap_matches_1 ); + preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_2, $cap_matches_2 ); + preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_3, $cap_matches_3 ); - // All should exist + $figure_id_1 = isset( $fig_matches_1[1] ) ? $fig_matches_1[1] : ''; + $figure_id_2 = isset( $fig_matches_2[1] ) ? $fig_matches_2[1] : ''; + $figure_id_3 = isset( $fig_matches_3[1] ) ? $fig_matches_3[1] : ''; + $caption_id_1 = isset( $cap_matches_1[1] ) ? $cap_matches_1[1] : ''; + $caption_id_2 = isset( $cap_matches_2[1] ) ? $cap_matches_2[1] : ''; + $caption_id_3 = isset( $cap_matches_3[1] ) ? $cap_matches_3[1] : ''; + + // Figure IDs should all exist + $this->assertNotEmpty( $figure_id_1, 'First figure should have an ID' ); + $this->assertNotEmpty( $figure_id_2, 'Second figure should have an ID' ); + $this->assertNotEmpty( $figure_id_3, 'Third figure should have an ID' ); + + // Figure IDs should all be different (each instance gets unique ID) + $this->assertNotSame( $figure_id_1, $figure_id_2, 'First and second figures should have different IDs even with identical content' ); + $this->assertNotSame( $figure_id_2, $figure_id_3, 'Second and third figures should have different IDs' ); + $this->assertNotSame( $figure_id_1, $figure_id_3, 'First and third figures should have different IDs' ); + + // Caption IDs should all exist $this->assertNotEmpty( $caption_id_1, 'First caption should have an ID' ); $this->assertNotEmpty( $caption_id_2, 'Second caption should have an ID' ); $this->assertNotEmpty( $caption_id_3, 'Third caption should have an ID' ); - // All should be different (each instance gets unique ID) + // Caption IDs should all be different (each instance gets unique ID) $this->assertNotSame( $caption_id_1, $caption_id_2, 'First and second captions should have different IDs even with identical content' ); $this->assertNotSame( $caption_id_2, $caption_id_3, 'Second and third captions should have different IDs' ); $this->assertNotSame( $caption_id_1, $caption_id_3, 'First and third captions should have different IDs' ); From 6fb659ae17bd98be8b50296d4b6fed99cc249acd Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Fri, 22 May 2026 01:51:52 -0700 Subject: [PATCH 04/12] fix: update breaking tests --- tests/phpunit/tests/media.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 5429b56746a45..2406f383f6f26 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -206,7 +206,7 @@ public function test_img_caption_shortcode_with_old_format_id_and_align() { ) ); $this->assertSame( 1, substr_count( $result, 'wp-caption &myAlignment' ) ); - $this->assertSame( 1, substr_count( $result, 'id="myId"' ) ); + $this->assertSame( 1, preg_match( '/id="myId-[0-9a-f]{2}"/', $result ) ); $this->assertSame( 1, substr_count( $result, self::CAPTION ) ); } @@ -302,7 +302,7 @@ public function test_img_caption_shortcode_has_aria_describedby() { self::IMG_CONTENT . self::HTML_CONTENT ); - $this->assertSame( 1, substr_count( $result, 'aria-describedby="caption-myId"' ) ); + $this->assertSame( 1, preg_match( '/aria-describedby="caption-myId-[0-9a-f]{2}"/', $result ) ); } /** From b5ebd2bd4a36bb40f6a6f19542e5face8e3f3641 Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Tue, 2 Jun 2026 18:13:03 -0700 Subject: [PATCH 05/12] refactor: use wp_unique_id as requested --- src/wp-includes/media.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 31832d6d53801..1c5d42b5fbbdf 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2599,11 +2599,8 @@ function img_caption_shortcode( $attr, $content = '' ) { $describedby = ''; if ( $atts['id'] ) { - // Add random component to figure ID to ensure uniqueness when - // same image appears multiple times on page. - $caption_id_random = '-' . substr( md5( microtime( true ) . wp_rand() ), 0, 2 ); - $atts['id'] = sanitize_html_class( $atts['id'] ); - $id = 'id="' . esc_attr( $atts['id'] . $caption_id_random ) . '" '; + $unique_id_value = wp_unique_id( sanitize_html_class( $atts['id'] ) ); + $id = 'id="' . esc_attr( $unique_id_value ) . '" '; } if ( $atts['caption_id'] ) { @@ -2613,8 +2610,9 @@ function img_caption_shortcode( $attr, $content = '' ) { } if ( $atts['caption_id'] ) { - $caption_id = 'id="' . esc_attr( $atts['caption_id'] . $caption_id_random ) . '" '; - $describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] . $caption_id_random ) . '" '; + $caption_id_value = wp_unique_id( $atts['caption_id'] ); + $caption_id = 'id="' . esc_attr( $caption_id_value ) . '" '; + $describedby = 'aria-describedby="' . esc_attr( $caption_id_value ) . '" '; } $class = trim( 'wp-caption ' . $atts['align'] . ' ' . $atts['class'] ); From 895f84c509e7aa7d2d2cf36588d031de45445e6c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 3 Jun 2026 07:24:19 +0200 Subject: [PATCH 06/12] Use void return type hint --- tests/phpunit/tests/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 2406f383f6f26..2dbf23f5b851d 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -314,7 +314,7 @@ public function test_img_caption_shortcode_has_aria_describedby() { * * @since 7.1.0 */ - public function test_img_caption_shortcode_unique_ids_per_instance() { + public function test_img_caption_shortcode_unique_ids_per_instance(): void { // First instance with caption "My caption" $result_1 = img_caption_shortcode( array( From 2181865cffac8b840c722d5a7df5aa6d90041471 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 3 Jun 2026 07:26:03 +0200 Subject: [PATCH 07/12] Use ticket reference instead of version reference --- tests/phpunit/tests/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 2dbf23f5b851d..ad6af4541e80b 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -312,7 +312,7 @@ public function test_img_caption_shortcode_has_aria_describedby() { * times on a page, each figure and figcaption should receive a unique ID to * maintain HTML validity and accessibility. * - * @since 7.1.0 + * @ticket 65315 */ public function test_img_caption_shortcode_unique_ids_per_instance(): void { // First instance with caption "My caption" From 184e429c6a2962844b055a74aebcb59e7f1c58c3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 3 Jun 2026 07:29:16 +0200 Subject: [PATCH 08/12] Fix caption shortcode test assertions to match wp_unique_id() output The assertions expected a "-[0-9a-f]{2}" suffix, but wp_unique_id() returns the prefix directly concatenated with a decimal counter (e.g. "myId1"), so the regexes never matched and the tests failed. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/phpunit/tests/media.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index ad6af4541e80b..f3305afd4dbb0 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -206,7 +206,7 @@ public function test_img_caption_shortcode_with_old_format_id_and_align() { ) ); $this->assertSame( 1, substr_count( $result, 'wp-caption &myAlignment' ) ); - $this->assertSame( 1, preg_match( '/id="myId-[0-9a-f]{2}"/', $result ) ); + $this->assertSame( 1, preg_match( '/id="myId[0-9]+"/', $result ) ); $this->assertSame( 1, substr_count( $result, self::CAPTION ) ); } @@ -302,7 +302,7 @@ public function test_img_caption_shortcode_has_aria_describedby() { self::IMG_CONTENT . self::HTML_CONTENT ); - $this->assertSame( 1, preg_match( '/aria-describedby="caption-myId-[0-9a-f]{2}"/', $result ) ); + $this->assertSame( 1, preg_match( '/aria-describedby="caption-myId[0-9]+"/', $result ) ); } /** From 17382236033a1a0fe2e4fc53859a4c616fad2196 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 3 Jun 2026 07:33:06 +0200 Subject: [PATCH 09/12] Use WP_HTML_Tag_Processor instead of regex to extract caption IDs in test Replace the brittle regex extraction in the per-instance uniqueness test with a WP_HTML_Tag_Processor helper that locates the caption wrapper and caption text elements by their class names, which is robust to both the HTML5 (figure/figcaption) and legacy (div/p) caption markup. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/phpunit/tests/media.php | 42 ++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index f3305afd4dbb0..9db778f1f83c1 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -345,22 +345,13 @@ public function test_img_caption_shortcode_unique_ids_per_instance(): void { self::IMG_CONTENT . 'Different caption' ); - // Extract figure IDs from results (format: id="attachment_123-XX") - preg_match( '/id="(attachment_123[^"]*)"/', $result_1, $fig_matches_1 ); - preg_match( '/id="(attachment_123[^"]*)"/', $result_2, $fig_matches_2 ); - preg_match( '/id="(attachment_123[^"]*)"/', $result_3, $fig_matches_3 ); - - // Extract figcaption IDs from results (format: id="caption-attachment-123-XX") - preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_1, $cap_matches_1 ); - preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_2, $cap_matches_2 ); - preg_match( '/id="([^"]*caption-attachment-123[^"]*)"/', $result_3, $cap_matches_3 ); - - $figure_id_1 = isset( $fig_matches_1[1] ) ? $fig_matches_1[1] : ''; - $figure_id_2 = isset( $fig_matches_2[1] ) ? $fig_matches_2[1] : ''; - $figure_id_3 = isset( $fig_matches_3[1] ) ? $fig_matches_3[1] : ''; - $caption_id_1 = isset( $cap_matches_1[1] ) ? $cap_matches_1[1] : ''; - $caption_id_2 = isset( $cap_matches_2[1] ) ? $cap_matches_2[1] : ''; - $caption_id_3 = isset( $cap_matches_3[1] ) ? $cap_matches_3[1] : ''; + // Extract the figure (caption wrapper) and caption text IDs from each instance. + $figure_id_1 = $this->get_id_of_first_tag_with_class( $result_1, 'wp-caption' ); + $figure_id_2 = $this->get_id_of_first_tag_with_class( $result_2, 'wp-caption' ); + $figure_id_3 = $this->get_id_of_first_tag_with_class( $result_3, 'wp-caption' ); + $caption_id_1 = $this->get_id_of_first_tag_with_class( $result_1, 'wp-caption-text' ); + $caption_id_2 = $this->get_id_of_first_tag_with_class( $result_2, 'wp-caption-text' ); + $caption_id_3 = $this->get_id_of_first_tag_with_class( $result_3, 'wp-caption-text' ); // Figure IDs should all exist $this->assertNotEmpty( $figure_id_1, 'First figure should have an ID' ); @@ -383,6 +374,25 @@ public function test_img_caption_shortcode_unique_ids_per_instance(): void { $this->assertNotSame( $caption_id_1, $caption_id_3, 'First and third captions should have different IDs' ); } + /** + * Returns the `id` attribute of the first tag bearing the given class name. + * + * @param string $html Markup to search. + * @param string $class_name Class name to locate the tag by. + * @return string|null The tag's `id` value, or null if no matching tag or `id` is found. + */ + private function get_id_of_first_tag_with_class( $html, $class_name ) { + $processor = new WP_HTML_Tag_Processor( $html ); + + if ( ! $processor->next_tag( array( 'class_name' => $class_name ) ) ) { + return null; + } + + $id = $processor->get_attribute( 'id' ); + + return is_string( $id ) ? $id : null; + } + public function test_add_remove_oembed_provider() { wp_oembed_add_provider( 'http://foo.bar/*', 'http://foo.bar/oembed' ); $this->assertTrue( wp_oembed_remove_provider( 'http://foo.bar/*' ) ); From 17a99766fde0f1f2f3bb91247998d1b9ee1b49df Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 3 Jun 2026 07:33:51 +0200 Subject: [PATCH 10/12] Add type hints to helper method --- tests/phpunit/tests/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 9db778f1f83c1..09343efae4ba2 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -381,7 +381,7 @@ public function test_img_caption_shortcode_unique_ids_per_instance(): void { * @param string $class_name Class name to locate the tag by. * @return string|null The tag's `id` value, or null if no matching tag or `id` is found. */ - private function get_id_of_first_tag_with_class( $html, $class_name ) { + private function get_id_of_first_tag_with_class( string $html, string $class_name ): ?string { $processor = new WP_HTML_Tag_Processor( $html ); if ( ! $processor->next_tag( array( 'class_name' => $class_name ) ) ) { From 270c11be289e900ca0da8f7759d0a248227d820c Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Wed, 3 Jun 2026 09:11:27 -0700 Subject: [PATCH 11/12] Update src/wp-includes/media.php Co-authored-by: Weston Ruter --- src/wp-includes/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 578267c149391..9a2d9ac1c75ed 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2599,7 +2599,7 @@ function img_caption_shortcode( $attr, $content = '' ) { $describedby = ''; if ( $atts['id'] ) { - $unique_id_value = wp_unique_id( sanitize_html_class( $atts['id'] ) ); + $unique_id_value = preg_replace( '/-1$/', '', wp_unique_prefixed_id( sanitize_html_class( $atts['id'] . '-' ) ) ); $id = 'id="' . esc_attr( $unique_id_value ) . '" '; } From 4fe064b5c5583a172e19e0dfd9ab491db4cafab5 Mon Sep 17 00:00:00 2001 From: Steel Wagstaff Date: Wed, 3 Jun 2026 09:13:07 -0700 Subject: [PATCH 12/12] fix: apply suggestions from code review Add increment count for reused IDs to ensure they are unique and avoid breaking previously implemented selectors. Co-authored-by: Weston Ruter --- src/wp-includes/media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 9a2d9ac1c75ed..5ef5bd638a9a9 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2610,7 +2610,7 @@ function img_caption_shortcode( $attr, $content = '' ) { } if ( $atts['caption_id'] ) { - $caption_id_value = wp_unique_id( $atts['caption_id'] ); + $caption_id_value = preg_replace( '/-1$/', '', wp_unique_id( $atts['caption_id'] . '-' ) ); $caption_id = 'id="' . esc_attr( $caption_id_value ) . '" '; $describedby = 'aria-describedby="' . esc_attr( $caption_id_value ) . '" '; }