forked from WordPress/wordpress-develop
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HTML API: Introduce HTML Template Renderer
Currently only renders text data: - does not render nested HTML (escapes everything) - does not escape URLs
- Loading branch information
Showing
4 changed files
with
140 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
<?php | ||
|
||
/** | ||
* WP_HTML_Template class. | ||
* | ||
* @since 6.5.0 | ||
*/ | ||
class WP_HTML_Template extends WP_HTML_Tag_Processor { | ||
/** | ||
* Renders an HTML template, replacing the placeholders with the provided values. | ||
* | ||
* @since 6.5.0 | ||
* | ||
* @param string $template The HTML template. | ||
* @param string $args Array of key/value pairs providing substitue values for the placeholders. | ||
* @return string The rendered HTML. | ||
*/ | ||
public static function render( $template, $args = array() ) { | ||
$processor = new self( $template ); | ||
while ( $processor->next_token() ) { | ||
$type = $processor->get_token_type(); | ||
$text = $processor->get_modifiable_text(); | ||
|
||
if ( '#funky-comment' === $type && strlen( $text ) > 0 && '%' === $text[0] ) { | ||
$name = substr( $text, 1 ); | ||
$value = isset( $args[ $name ] ) && is_string( $args[ $name ] ) ? $args[ $name ] : null; | ||
$processor->set_bookmark( 'here' ); | ||
$processor->lexical_updates[] = new WP_HTML_Text_Replacement( | ||
$processor->bookmarks['here']->start, | ||
$processor->bookmarks['here']->length, | ||
null === $value ? '' : esc_html( $value ) | ||
); | ||
} | ||
|
||
if ( '#tag' === $type ) { | ||
foreach ( $processor->get_attribute_names_with_prefix( '' ) ?? array() as $attribute_name ) { | ||
if ( str_starts_with( $attribute_name, '...' ) ) { | ||
$spread_name = substr( $attribute_name, 3 ); | ||
if ( isset( $args[ $spread_name ] ) && is_array( $args[ $spread_name ] ) ) { | ||
foreach ( $args[ $spread_name ] as $key => $value ) { | ||
if ( true === $value || null === $value || is_string( $value ) ) { | ||
$processor->set_attribute( $key, $value ); | ||
} | ||
} | ||
} | ||
$processor->remove_attribute( $attribute_name ); | ||
} | ||
|
||
$value = $processor->get_attribute( $attribute_name ); | ||
|
||
if ( ! is_string( $value ) ) { | ||
continue; | ||
} | ||
|
||
$full_match = null; | ||
if ( preg_match( '~^</%([^>]+)>$~', $value, $full_match ) ) { | ||
$name = $full_match[1]; | ||
|
||
if ( array_key_exists( $name, $args ) ) { | ||
$value = $args[ $name ]; | ||
if ( null === $value ) { | ||
$processor->remove_attribute( $attribute_name ); | ||
} elseif ( true === $value ) { | ||
$processor->set_attribute( $attribute_name, true ); | ||
} elseif ( is_string( $value ) ) { | ||
$processor->set_attribute( $attribute_name, esc_attr( $args[ $name ] ) ); | ||
} else { | ||
$processor->remove_attribute( $attribute_name ); | ||
} | ||
} else { | ||
$processor->remove_attribute( $attribute_name ); | ||
} | ||
|
||
continue; | ||
} | ||
|
||
$new_value = preg_replace_callback( | ||
'~</%([^>]+)>~', | ||
static function ( $matches ) use ( $args ) { | ||
return is_string( $args[ $matches[1] ] ) | ||
? esc_attr( $args[ $matches[1] ] ) | ||
: ''; | ||
}, | ||
$value | ||
); | ||
|
||
if ( $new_value !== $value ) { | ||
$processor->set_attribute( $attribute_name, $new_value ); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return $processor->get_updated_html(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
/** | ||
* Unit tests covering WP_HTML_Template functionality. | ||
* | ||
* @package WordPress | ||
* @subpackage HTML-API | ||
* | ||
* @since 6.5.0 | ||
* | ||
* @group html-api | ||
* | ||
* @coversDefaultClass WP_HTML_Template | ||
*/ | ||
|
||
class Tests_HtmlApi_WpHtmlTemplate extends WP_UnitTestCase { | ||
/** | ||
* Demonstrates how to pass values into an HTML template. | ||
* | ||
* @ticket {TICKET_NUMBER} | ||
*/ | ||
public function test_basic_render() { | ||
$html = WP_HTML_Template::render( | ||
'<div class="is-test </%class>" ...div-args inert="</%is_inert>">Just a </%count> test</div>', | ||
array( | ||
'count' => '<strong>Hi <3</strong>', | ||
'class' => '5>4', | ||
'is_inert' => 'inert', | ||
'div-args' => array( | ||
'class' => 'hoover', | ||
'disabled' => true, | ||
), | ||
) | ||
); | ||
|
||
$this->assertSame( | ||
'<div disabled class="hoover" inert="inert">Just a <strong>Hi <3</strong> test</div>', | ||
$html, | ||
'Failed to properly render template.' | ||
); | ||
} | ||
} |