Skip to content

Commit 3806b25

Browse files
committed
HTML API: Revert using regex in block bindings HTML replacement logic.
This changeset reverts part of the changes made in [58298] to avoid using regex that can cause potential bugs. It is indeed safer to revert these changes for now and do the refactoring once the HTML API supports CSS selectors and provides a way to set inner content. It also adds a unit test to cover the regression experienced in WordPress/gutenberg#62347. Follow-up to [58298]. Props santosguillamot, gziolo. Fixes #61385. See #61351. git-svn-id: https://develop.svn.wordpress.org/trunk@58398 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 459d996 commit 3806b25

File tree

2 files changed

+94
-17
lines changed

2 files changed

+94
-17
lines changed

src/wp-includes/class-wp-block.php

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -333,27 +333,68 @@ private function replace_html( string $block_content, string $attribute_name, $s
333333
switch ( $block_type->attributes[ $attribute_name ]['source'] ) {
334334
case 'html':
335335
case 'rich-text':
336-
// Hardcode the selectors and processing until the HTML API is able to read CSS selectors and replace inner HTML.
337-
// TODO: Use the HTML API instead.
338-
if ( 'core/paragraph' === $this->name && 'content' === $attribute_name ) {
339-
$selector = 'p';
340-
}
341-
if ( 'core/heading' === $this->name && 'content' === $attribute_name ) {
342-
$selector = 'h[1-6]';
336+
$block_reader = new WP_HTML_Tag_Processor( $block_content );
337+
338+
// TODO: Support for CSS selectors whenever they are ready in the HTML API.
339+
// In the meantime, support comma-separated selectors by exploding them into an array.
340+
$selectors = explode( ',', $block_type->attributes[ $attribute_name ]['selector'] );
341+
// Add a bookmark to the first tag to be able to iterate over the selectors.
342+
$block_reader->next_tag();
343+
$block_reader->set_bookmark( 'iterate-selectors' );
344+
345+
// TODO: This shouldn't be needed when the `set_inner_html` function is ready.
346+
// Store the parent tag and its attributes to be able to restore them later in the button.
347+
// The button block has a wrapper while the paragraph and heading blocks don't.
348+
if ( 'core/button' === $this->name ) {
349+
$button_wrapper = $block_reader->get_tag();
350+
$button_wrapper_attribute_names = $block_reader->get_attribute_names_with_prefix( '' );
351+
$button_wrapper_attrs = array();
352+
foreach ( $button_wrapper_attribute_names as $name ) {
353+
$button_wrapper_attrs[ $name ] = $block_reader->get_attribute( $name );
354+
}
343355
}
344-
if ( 'core/button' === $this->name && 'text' === $attribute_name ) {
345-
// Check if it is a <button> or <a> tag.
346-
if ( preg_match( '/<button[^>]*>.*?<\/button>/', $block_content ) ) {
347-
$selector = 'button';
356+
357+
foreach ( $selectors as $selector ) {
358+
// If the parent tag, or any of its children, matches the selector, replace the HTML.
359+
if ( strcasecmp( $block_reader->get_tag( $selector ), $selector ) === 0 || $block_reader->next_tag(
360+
array(
361+
'tag_name' => $selector,
362+
)
363+
) ) {
364+
$block_reader->release_bookmark( 'iterate-selectors' );
365+
366+
// TODO: Use `set_inner_html` method whenever it's ready in the HTML API.
367+
// Until then, it is hardcoded for the paragraph, heading, and button blocks.
368+
// Store the tag and its attributes to be able to restore them later.
369+
$selector_attribute_names = $block_reader->get_attribute_names_with_prefix( '' );
370+
$selector_attrs = array();
371+
foreach ( $selector_attribute_names as $name ) {
372+
$selector_attrs[ $name ] = $block_reader->get_attribute( $name );
373+
}
374+
$selector_markup = "<$selector>" . wp_kses_post( $source_value ) . "</$selector>";
375+
$amended_content = new WP_HTML_Tag_Processor( $selector_markup );
376+
$amended_content->next_tag();
377+
foreach ( $selector_attrs as $attribute_key => $attribute_value ) {
378+
$amended_content->set_attribute( $attribute_key, $attribute_value );
379+
}
380+
if ( 'core/paragraph' === $this->name || 'core/heading' === $this->name ) {
381+
return $amended_content->get_updated_html();
382+
}
383+
if ( 'core/button' === $this->name ) {
384+
$button_markup = "<$button_wrapper>{$amended_content->get_updated_html()}</$button_wrapper>";
385+
$amended_button = new WP_HTML_Tag_Processor( $button_markup );
386+
$amended_button->next_tag();
387+
foreach ( $button_wrapper_attrs as $attribute_key => $attribute_value ) {
388+
$amended_button->set_attribute( $attribute_key, $attribute_value );
389+
}
390+
return $amended_button->get_updated_html();
391+
}
348392
} else {
349-
$selector = 'a';
393+
$block_reader->seek( 'iterate-selectors' );
350394
}
351395
}
352-
if ( empty( $selector ) ) {
353-
return $block_content;
354-
}
355-
$pattern = '/(<' . $selector . '[^>]*>).*?(<\/' . $selector . '>)/i';
356-
return preg_replace( $pattern, '$1' . wp_kses_post( $source_value ) . '$2', $block_content );
396+
$block_reader->release_bookmark( 'iterate-selectors' );
397+
return $block_content;
357398

358399
case 'attribute':
359400
$amended_content = new WP_HTML_Tag_Processor( $block_content );

tests/phpunit/tests/block-bindings/render.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,42 @@ public function test_source_value_with_unsafe_html_is_sanitized() {
235235
);
236236
}
237237

238+
/**
239+
* Tests that including symbols and numbers works well with bound attributes.
240+
*
241+
* @ticket 61385
242+
*
243+
* @covers WP_Block::process_block_bindings
244+
*/
245+
public function test_using_symbols_in_block_bindings_value() {
246+
$get_value_callback = function () {
247+
return '$12.50';
248+
};
249+
250+
register_block_bindings_source(
251+
self::SOURCE_NAME,
252+
array(
253+
'label' => self::SOURCE_LABEL,
254+
'get_value_callback' => $get_value_callback,
255+
)
256+
);
257+
258+
$block_content = <<<HTML
259+
<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source"}}}} -->
260+
<p>Default content</p>
261+
<!-- /wp:paragraph -->
262+
HTML;
263+
$parsed_blocks = parse_blocks( $block_content );
264+
$block = new WP_Block( $parsed_blocks[0] );
265+
$result = $block->render();
266+
267+
$this->assertSame(
268+
'<p>$12.50</p>',
269+
trim( $result ),
270+
'The block content should properly show the symbol and numbers.'
271+
);
272+
}
273+
238274
/**
239275
* Tests if the `__default` attribute is replaced with real attribues for
240276
* pattern overrides.

0 commit comments

Comments
 (0)