diff --git a/src/wp-includes/html-api/class-wp-html-template.php b/src/wp-includes/html-api/class-wp-html-template.php index 3169b10dfc317..14ee1b3c670e9 100644 --- a/src/wp-includes/html-api/class-wp-html-template.php +++ b/src/wp-includes/html-api/class-wp-html-template.php @@ -26,7 +26,7 @@ class WP_HTML_Template extends WP_HTML_Tag_Processor { * * This function looks for placeholders in the template string and will replace * them with appropriately-escaped substitutions from the given arguments, if - * provided and if those arguments are strings. + * provided and if those arguments are strings or valid attribute values. * * Example: * @@ -64,7 +64,7 @@ class WP_HTML_Template extends WP_HTML_Tag_Processor { * If provided any other type of value the attribute will be ignored and its existing value persists. * * - If multiple HTML attributes are specified for a given tag they will be applied as if calling - * `set_attribute()` in the order they are specified in the temlpate. This includes any attributes + * `set_attribute()` in the order they are specified in the template. This includes any attributes * assigned through the attribute spread syntax. * * - Substitutions in text nodes may only contain string values. If provided any other type of value @@ -74,7 +74,7 @@ class WP_HTML_Template extends WP_HTML_Tag_Processor { * it may provide the ability to nest pre-rendered HTML into the template, but this functionality * is deferred for a future update. * - * - This function will not replace content inside of TEXTAREA, TITLE, SCRIPT, or STYLE elements. + * - This function will not replace content inside of SCRIPT, or STYLE elements. * * @since 6.5.0 * @@ -162,6 +162,25 @@ static function ( $matches ) use ( $args ) { $processor->set_attribute( $attribute_name, $new_value ); } } + + // Update TEXTAREA and TITLE contents. + $tag_name = $processor->get_tag(); + if ( 'TEXTAREA' === $tag_name || 'TITLE' === $tag_name ) { + // Replace placeholders inside these RCDATA tags. + $new_text = preg_replace_callback( + '~]+)>~', + static function ( $matches ) use ( $args ) { + return is_string( $args[ $matches[1] ] ) + ? $args[ $matches[1] ] + : ''; + }, + $text + ); + + if ( $new_text !== $text ) { + $processor->set_modifiable_text( $new_text ); + } + } } } diff --git a/src/wp-includes/html-api/class-wp-html.php b/src/wp-includes/html-api/class-wp-html.php index 6443acac91508..a1c38e866af59 100644 --- a/src/wp-includes/html-api/class-wp-html.php +++ b/src/wp-includes/html-api/class-wp-html.php @@ -20,7 +20,7 @@ class WP_HTML { * * This function looks for placeholders in the template string and will replace * them with appropriately-escaped substitutions from the given arguments, if - * provided and if those arguments are strings. + * provided and if those arguments are strings or valid attribute values. * * Example: * @@ -58,7 +58,7 @@ class WP_HTML { * If provided any other type of value the attribute will be ignored and its existing value persists. * * - If multiple HTML attributes are specified for a given tag they will be applied as if calling - * `set_attribute()` in the order they are specified in the temlpate. This includes any attributes + * `set_attribute()` in the order they are specified in the template. This includes any attributes * assigned through the attribute spread syntax. * * - Substitutions in text nodes may only contain string values. If provided any other type of value @@ -68,6 +68,8 @@ class WP_HTML { * it may provide the ability to nest pre-rendered HTML into the template, but this functionality * is deferred for a future update. * + * - This function will not replace content inside of SCRIPT, or STYLE elements. + * * @since 6.5.0 * * @access private diff --git a/tests/phpunit/tests/html-api/wpHtmlTemplate.php b/tests/phpunit/tests/html-api/wpHtmlTemplate.php index cc63eba954fd2..dd4859fb2f14b 100644 --- a/tests/phpunit/tests/html-api/wpHtmlTemplate.php +++ b/tests/phpunit/tests/html-api/wpHtmlTemplate.php @@ -85,4 +85,22 @@ public function test_cannot_break_out_of_tag_with_malicious_attribute_name() { "Should not have found any other tags but found {$processor->get_tag()} instead." ); } + + /** + * Ensures that basic replacement inside a TEXTAREA subtitutes placeholders. + * + * @ticket 60229 + */ + public function test_replaces_textarea_placeholders() { + $html = WP_HTML_Template::render( + '', + array( 'big' => ' ()' ) + ); + + $this->assertSame( + '', + $html, + 'Should have replaced placeholder with RCDATA escaping rules.' + ); + } }