Skip to content

Commit 5eceb50

Browse files
authored
Merge pull request #1244 from WordPress/update/do-blocks-parsing-and-autop
Fix parsing in do_blocks() and rendering of blocks on frontend in the_content
2 parents aaae523 + 6a68752 commit 5eceb50

File tree

2 files changed

+68
-21
lines changed

2 files changed

+68
-21
lines changed

lib/blocks.php

+32-14
Original file line numberDiff line numberDiff line change
@@ -102,28 +102,46 @@ function do_blocks( $content ) {
102102
global $wp_registered_blocks;
103103

104104
// Extract the blocks from the post content.
105-
$matcher = '/<!--\s*wp:([a-z](?:[a-z0-9\/]+)*)\s+((?:(?!-->).)*)\s*\/?-->(?:.*?<!--\s*\/wp:\g1\s+-->)?/s';
105+
$matcher = '#' . join( '', array(
106+
'(?P<opener><!--\s*',
107+
'wp:(?P<block_name>[a-z](?:[a-z0-9/]+)*)\s+',
108+
'(?P<attributes>(?:(?!-->).)*)',
109+
'\s*/?-->\n?)',
110+
'(?:',
111+
'(?P<content>.*?)',
112+
'(?P<closer><!--\s*/wp:\g{block_name}\s+-->\n?)',
113+
')?',
114+
) ) . '#s';
106115
preg_match_all( $matcher, $content, $matches, PREG_OFFSET_CAPTURE );
107116

108117
$new_content = $content;
118+
$offset_differential = 0;
109119
foreach ( $matches[0] as $index => $block_match ) {
110-
$block_name = $matches[1][ $index ][0];
111-
// do nothing if the block is not registered.
112-
if ( ! isset( $wp_registered_blocks[ $block_name ] ) ) {
113-
continue;
114-
}
120+
$block_name = $matches['block_name'][ $index ][0];
121+
122+
$output = '';
123+
if ( isset( $wp_registered_blocks[ $block_name ] ) ) {
124+
$block_attributes_string = $matches['attributes'][ $index ][0];
125+
$block_attributes = parse_block_attributes( $block_attributes_string );
115126

116-
$block_markup = $block_match[0];
117-
$block_attributes_string = $matches[2][ $index ][0];
118-
$block_attributes = parse_block_attributes( $block_attributes_string );
127+
// Call the block's render function to generate the dynamic output.
128+
$output = call_user_func( $wp_registered_blocks[ $block_name ]['render'], $block_attributes );
129+
} elseif ( isset( $matches['content'][ $index ][0] ) ) {
130+
$output = $matches['content'][ $index ][0];
131+
}
119132

120-
// Call the block's render function to generate the dynamic output.
121-
$output = call_user_func( $wp_registered_blocks[ $block_name ]['render'], $block_attributes );
133+
// Replace the matched block with the static or dynamic output.
134+
$new_content = substr_replace(
135+
$new_content,
136+
$output,
137+
$block_match[1] - $offset_differential,
138+
strlen( $block_match[0] )
139+
);
122140

123-
// Replace the matched block with the dynamic output.
124-
$new_content = str_replace( $block_markup, $output, $new_content );
141+
// Update offset for the next replacement.
142+
$offset_differential += strlen( $block_match[0] ) - strlen( $output );
125143
}
126144

127145
return $new_content;
128146
}
129-
add_filter( 'the_content', 'do_blocks', 10 ); // BEFORE do_shortcode().
147+
add_filter( 'the_content', 'do_blocks', 9 ); // BEFORE do_shortcode() and wpautop().

phpunit/class-dynamic-blocks-render-test.php

+36-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
* Test do_blocks
1010
*/
1111
class Dynamic_Blocks_Render_Test extends WP_UnitTestCase {
12+
13+
/**
14+
* Dummy block instance number.
15+
*
16+
* @var int
17+
*/
18+
protected $dummy_block_instance_number = 0;
19+
1220
/**
1321
* Dummy block rendering function.
1422
*
@@ -17,13 +25,23 @@ class Dynamic_Blocks_Render_Test extends WP_UnitTestCase {
1725
* @return string Block output.
1826
*/
1927
function render_dummy_block( $attributes ) {
20-
return $attributes['value'];
28+
$this->dummy_block_instance_number += 1;
29+
return $this->dummy_block_instance_number . ':' . $attributes['value'];
2130
}
2231

32+
/**
33+
* Tear down.
34+
*/
2335
function tearDown() {
36+
$this->dummy_block_instance_number = 0;
2437
$GLOBALS['wp_registered_blocks'] = array();
2538
}
2639

40+
/**
41+
* Test dynamic blocks that lack content, including void blocks.
42+
*
43+
* @covers do_blocks
44+
*/
2745
function test_dynamic_block_rendering() {
2846
$settings = array(
2947
'render' => array(
@@ -32,23 +50,34 @@ function test_dynamic_block_rendering() {
3250
),
3351
);
3452
register_block_type( 'core/dummy', $settings );
53+
54+
// The duplicated dynamic blocks below are there to ensure that do_blocks() replaces each one-by-one.
3555
$post_content =
3656
'before' .
3757
'<!-- wp:core/dummy value="b1" --><!-- /wp:core/dummy -->' .
58+
'<!-- wp:core/dummy value="b1" --><!-- /wp:core/dummy -->' .
3859
'between' .
39-
'<!-- wp:core/dummy value="b2" --><!-- /wp:core/dummy -->' .
60+
'<!-- wp:core/dummy value="b2" /-->' .
61+
'<!-- wp:core/dummy value="b2" /-->' .
4062
'after';
4163

4264
$updated_post_content = do_blocks( $post_content );
4365
$this->assertEquals( $updated_post_content,
4466
'before' .
45-
'b1' .
67+
'1:b1' .
68+
'2:b1' .
4669
'between' .
47-
'b2' .
70+
'3:b2' .
71+
'4:b2' .
4872
'after'
4973
);
5074
}
5175

76+
/**
77+
* Test dynamic blocks that contain content.
78+
*
79+
* @covers do_blocks
80+
*/
5281
function test_dynamic_block_rendering_with_content() {
5382
$settings = array(
5483
'render' => array(
@@ -59,17 +88,17 @@ function test_dynamic_block_rendering_with_content() {
5988
register_block_type( 'core/dummy', $settings );
6089
$post_content =
6190
'before' .
62-
'<!-- wp:core/dummy value="b1" -->this should be ignored<!-- /wp:core/dummy -->' .
91+
"<!-- wp:core/dummy value=\"b1\" -->this\nshould\n\nbe\nignored<!-- /wp:core/dummy -->" .
6392
'between' .
6493
'<!-- wp:core/dummy value="b2" -->this should also be ignored<!-- /wp:core/dummy -->' .
6594
'after';
6695

6796
$updated_post_content = do_blocks( $post_content );
6897
$this->assertEquals( $updated_post_content,
6998
'before' .
70-
'b1' .
99+
'1:b1' .
71100
'between' .
72-
'b2' .
101+
'2:b2' .
73102
'after'
74103
);
75104
}

0 commit comments

Comments
 (0)