diff --git a/lib/blocks.php b/lib/blocks.php index b1861fc281f9a7..5190dac4689583 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -102,28 +102,46 @@ function do_blocks( $content ) { global $wp_registered_blocks; // Extract the blocks from the post content. - $matcher = '/).)*)\s*\/?-->(?:.*?)?/s'; + $matcher = '#' . join( '', array( + '(?P).)*)', + '\s*/?-->\n?)', + '(?:', + '(?P.*?)', + '(?P\n?)', + ')?', + ) ) . '#s'; preg_match_all( $matcher, $content, $matches, PREG_OFFSET_CAPTURE ); $new_content = $content; + $offset_differential = 0; foreach ( $matches[0] as $index => $block_match ) { - $block_name = $matches[1][ $index ][0]; - // do nothing if the block is not registered. - if ( ! isset( $wp_registered_blocks[ $block_name ] ) ) { - continue; - } + $block_name = $matches['block_name'][ $index ][0]; + + $output = ''; + if ( isset( $wp_registered_blocks[ $block_name ] ) ) { + $block_attributes_string = $matches['attributes'][ $index ][0]; + $block_attributes = parse_block_attributes( $block_attributes_string ); - $block_markup = $block_match[0]; - $block_attributes_string = $matches[2][ $index ][0]; - $block_attributes = parse_block_attributes( $block_attributes_string ); + // Call the block's render function to generate the dynamic output. + $output = call_user_func( $wp_registered_blocks[ $block_name ]['render'], $block_attributes ); + } elseif ( isset( $matches['content'][ $index ][0] ) ) { + $output = $matches['content'][ $index ][0]; + } - // Call the block's render function to generate the dynamic output. - $output = call_user_func( $wp_registered_blocks[ $block_name ]['render'], $block_attributes ); + // Replace the matched block with the static or dynamic output. + $new_content = substr_replace( + $new_content, + $output, + $block_match[1] - $offset_differential, + strlen( $block_match[0] ) + ); - // Replace the matched block with the dynamic output. - $new_content = str_replace( $block_markup, $output, $new_content ); + // Update offset for the next replacement. + $offset_differential += strlen( $block_match[0] ) - strlen( $output ); } return $new_content; } -add_filter( 'the_content', 'do_blocks', 10 ); // BEFORE do_shortcode(). +add_filter( 'the_content', 'do_blocks', 9 ); // BEFORE do_shortcode() and wpautop(). diff --git a/phpunit/class-dynamic-blocks-render-test.php b/phpunit/class-dynamic-blocks-render-test.php index 60b6de3d729e61..7e557ed73986d5 100644 --- a/phpunit/class-dynamic-blocks-render-test.php +++ b/phpunit/class-dynamic-blocks-render-test.php @@ -9,6 +9,14 @@ * Test do_blocks */ class Dynamic_Blocks_Render_Test extends WP_UnitTestCase { + + /** + * Dummy block instance number. + * + * @var int + */ + protected $dummy_block_instance_number = 0; + /** * Dummy block rendering function. * @@ -17,13 +25,23 @@ class Dynamic_Blocks_Render_Test extends WP_UnitTestCase { * @return string Block output. */ function render_dummy_block( $attributes ) { - return $attributes['value']; + $this->dummy_block_instance_number += 1; + return $this->dummy_block_instance_number . ':' . $attributes['value']; } + /** + * Tear down. + */ function tearDown() { + $this->dummy_block_instance_number = 0; $GLOBALS['wp_registered_blocks'] = array(); } + /** + * Test dynamic blocks that lack content, including void blocks. + * + * @covers do_blocks + */ function test_dynamic_block_rendering() { $settings = array( 'render' => array( @@ -32,23 +50,34 @@ function test_dynamic_block_rendering() { ), ); register_block_type( 'core/dummy', $settings ); + + // The duplicated dynamic blocks below are there to ensure that do_blocks() replaces each one-by-one. $post_content = 'before' . '' . + '' . 'between' . - '' . + '' . + '' . 'after'; $updated_post_content = do_blocks( $post_content ); $this->assertEquals( $updated_post_content, 'before' . - 'b1' . + '1:b1' . + '2:b1' . 'between' . - 'b2' . + '3:b2' . + '4:b2' . 'after' ); } + /** + * Test dynamic blocks that contain content. + * + * @covers do_blocks + */ function test_dynamic_block_rendering_with_content() { $settings = array( 'render' => array( @@ -59,7 +88,7 @@ function test_dynamic_block_rendering_with_content() { register_block_type( 'core/dummy', $settings ); $post_content = 'before' . - 'this should be ignored' . + "this\nshould\n\nbe\nignored" . 'between' . 'this should also be ignored' . 'after'; @@ -67,9 +96,9 @@ function test_dynamic_block_rendering_with_content() { $updated_post_content = do_blocks( $post_content ); $this->assertEquals( $updated_post_content, 'before' . - 'b1' . + '1:b1' . 'between' . - 'b2' . + '2:b2' . 'after' ); }