From f51b8ec077f22553c1b5bde87ee955bccc3f4e19 Mon Sep 17 00:00:00 2001 From: Patrick Piwowarczyk Date: Thu, 9 Oct 2025 15:06:30 -0500 Subject: [PATCH 1/6] Fix sanitization of non-breaking hyphens in sanitize_title_with_dashes Adds URL-encoded non-breaking hyphen () to the list of characters converted to regular hyphens in sanitize_title_with_dashes() Fixes ticket #64089 --- src/wp-includes/formatting.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 2f22ec25f8738..7c3fe7e21d1bd 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2294,8 +2294,8 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa $title = strtolower( $title ); if ( 'save' === $context ) { - // Convert  , &ndash, and &mdash to hyphens. - $title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title ); + // Convert  , ‑ (non-breaking hyphen), &ndash, and &mdash to hyphens. + $title = str_replace( array( '%c2%a0', '%e2%80%91', '%e2%80%93', '%e2%80%94' ), '-', $title ); // Convert  , &ndash, and &mdash HTML entities to hyphens. $title = str_replace( array( ' ', ' ', '–', '–', '—', '—' ), '-', $title ); // Convert forward slash to hyphen. From b6c307f5713659d25a4dfc419108c3e1c3ea7013 Mon Sep 17 00:00:00 2001 From: Patrick Piwowarczyk Date: Wed, 22 Oct 2025 00:12:04 -0500 Subject: [PATCH 2/6] Sanitize non-breaking hypen HTML entity code in sanitize_title_with_dashes --- src/wp-includes/formatting.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 7c3fe7e21d1bd..ba6066c47c5f9 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2294,10 +2294,10 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa $title = strtolower( $title ); if ( 'save' === $context ) { - // Convert  , ‑ (non-breaking hyphen), &ndash, and &mdash to hyphens. + // Convert  , non-breaking hyphen, &ndash, and &mdash to hyphens. $title = str_replace( array( '%c2%a0', '%e2%80%91', '%e2%80%93', '%e2%80%94' ), '-', $title ); - // Convert  , &ndash, and &mdash HTML entities to hyphens. - $title = str_replace( array( ' ', ' ', '–', '–', '—', '—' ), '-', $title ); + // Convert  , non-breaking hyphen, &ndash, and &mdash HTML entities to hyphens. + $title = str_replace( array( ' ', '‑', ' ', '–', '–', '—', '—' ), '-', $title ); // Convert forward slash to hyphen. $title = str_replace( '/', '-', $title ); From 4a8e4bf02d5dd460fad43af848b7305c21996da8 Mon Sep 17 00:00:00 2001 From: Patrick Piwowarczyk Date: Wed, 22 Oct 2025 00:27:52 -0500 Subject: [PATCH 3/6] Add test cases for #64089 --- .../tests/formatting/sanitizeTitleWithDashes.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php b/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php index 5c7a81cb2974f..4fbcb8adf7ca6 100644 --- a/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php +++ b/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php @@ -85,6 +85,18 @@ public function test_replaces_ndash_mdash_entities() { $this->assertSame( 'do-the-dash', sanitize_title_with_dashes( 'Do — the — Dash', '', 'save' ) ); } + + /** + * @ticket 64089 + */ + public function test_replaces_nonbreaking_hyphen() { + $this->assertSame( 'do-the-dash', sanitize_title_with_dashes( 'Do‑the Dash', '', 'save' ) ); + } + + public function test_replaces_nonbreaking_hyphen_entity() { + $this->assertSame( 'do-the-dash', sanitize_title_with_dashes( 'Do ‑ the Dash', '', 'save' ) ); + } + public function test_replaces_iexcel_iquest() { $this->assertSame( 'just-a-slug', sanitize_title_with_dashes( 'Just ¡a Slug', '', 'save' ) ); $this->assertSame( 'just-a-slug', sanitize_title_with_dashes( 'Just a Slug¿', '', 'save' ) ); From 7c2fa719b444ed905739e44fe3b21e99890646e1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 22 Oct 2025 16:12:23 -0700 Subject: [PATCH 4/6] Tidy up test --- .../phpunit/tests/formatting/sanitizeTitleWithDashes.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php b/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php index 4fbcb8adf7ca6..8a2ee4f9d9fee 100644 --- a/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php +++ b/tests/phpunit/tests/formatting/sanitizeTitleWithDashes.php @@ -85,15 +85,17 @@ public function test_replaces_ndash_mdash_entities() { $this->assertSame( 'do-the-dash', sanitize_title_with_dashes( 'Do — the — Dash', '', 'save' ) ); } - /** * @ticket 64089 */ - public function test_replaces_nonbreaking_hyphen() { + public function test_replaces_non_breaking_hyphen() { $this->assertSame( 'do-the-dash', sanitize_title_with_dashes( 'Do‑the Dash', '', 'save' ) ); } - public function test_replaces_nonbreaking_hyphen_entity() { + /** + * @ticket 64089 + */ + public function test_replaces_non_breaking_hyphen_entity() { $this->assertSame( 'do-the-dash', sanitize_title_with_dashes( 'Do ‑ the Dash', '', 'save' ) ); } From ff2d2a730144328591c4f654c41e06ad8499a7f6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 22 Oct 2025 17:05:43 -0700 Subject: [PATCH 5/6] Refactor replacement of dashes to hyphens --- src/wp-includes/formatting.php | 40 +++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index ba6066c47c5f9..b3cf63c6b8f78 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2294,12 +2294,42 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa $title = strtolower( $title ); if ( 'save' === $context ) { - // Convert  , non-breaking hyphen, &ndash, and &mdash to hyphens. - $title = str_replace( array( '%c2%a0', '%e2%80%91', '%e2%80%93', '%e2%80%94' ), '-', $title ); - // Convert  , non-breaking hyphen, &ndash, and &mdash HTML entities to hyphens. - $title = str_replace( array( ' ', '‑', ' ', '–', '–', '—', '—' ), '-', $title ); + // Mapping of Unicode hex codepoint to HTML named entity (if it exists). + $dash_characters = array( + // Non-breaking space (U+00A0). + 'A0' => 'nbsp', + + // Non-breaking hyphen (U+2011). + '2011' => null, + + // En dash (U+2013). + '2013' => 'ndash', + + // Em dash (U+2014). + '2014' => 'mdash', + ); + + // Convert dashes to hyphens. + $replacements = array(); + foreach ( $dash_characters as $hex_codepoint => $named_entity ) { + // HTML entities. + $replacements[] = '&#x' . $hex_codepoint . ';'; + if ( $named_entity ) { + $replacements[] = '&' . $named_entity . ';'; + } + $decimal_codepoint = hexdec( $hex_codepoint ); + $replacements[] = '&#' . $decimal_codepoint . ';'; + + // URL-encoded characters. + if ( function_exists( 'mb_chr' ) ) { + $replacements[] = rawurlencode( mb_chr( $decimal_codepoint, 'UTF-8' ) ); + } + } + // Convert forward slash to hyphen. - $title = str_replace( '/', '-', $title ); + $replacements[] = '/'; + + $title = str_ireplace( $replacements, '-', $title ); // Strip these characters entirely. $title = str_replace( From 7c52413394d8e740b279639ecd57a609bb3df5c3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 24 Oct 2025 22:25:16 -0700 Subject: [PATCH 6/6] Revert "Refactor replacement of dashes to hyphens" This reverts commit ff2d2a730144328591c4f654c41e06ad8499a7f6. --- src/wp-includes/formatting.php | 40 +++++----------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index b3cf63c6b8f78..ba6066c47c5f9 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2294,42 +2294,12 @@ function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'displa $title = strtolower( $title ); if ( 'save' === $context ) { - // Mapping of Unicode hex codepoint to HTML named entity (if it exists). - $dash_characters = array( - // Non-breaking space (U+00A0). - 'A0' => 'nbsp', - - // Non-breaking hyphen (U+2011). - '2011' => null, - - // En dash (U+2013). - '2013' => 'ndash', - - // Em dash (U+2014). - '2014' => 'mdash', - ); - - // Convert dashes to hyphens. - $replacements = array(); - foreach ( $dash_characters as $hex_codepoint => $named_entity ) { - // HTML entities. - $replacements[] = '&#x' . $hex_codepoint . ';'; - if ( $named_entity ) { - $replacements[] = '&' . $named_entity . ';'; - } - $decimal_codepoint = hexdec( $hex_codepoint ); - $replacements[] = '&#' . $decimal_codepoint . ';'; - - // URL-encoded characters. - if ( function_exists( 'mb_chr' ) ) { - $replacements[] = rawurlencode( mb_chr( $decimal_codepoint, 'UTF-8' ) ); - } - } - + // Convert  , non-breaking hyphen, &ndash, and &mdash to hyphens. + $title = str_replace( array( '%c2%a0', '%e2%80%91', '%e2%80%93', '%e2%80%94' ), '-', $title ); + // Convert  , non-breaking hyphen, &ndash, and &mdash HTML entities to hyphens. + $title = str_replace( array( ' ', '‑', ' ', '–', '–', '—', '—' ), '-', $title ); // Convert forward slash to hyphen. - $replacements[] = '/'; - - $title = str_ireplace( $replacements, '-', $title ); + $title = str_replace( '/', '-', $title ); // Strip these characters entirely. $title = str_replace(