Skip to content

Commit

Permalink
Parsing: Use full parser in do_blocks with nested block support
Browse files Browse the repository at this point in the history
  • Loading branch information
dmsnell committed Oct 27, 2018
1 parent 0faf9ad commit 4a4feba
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 76 deletions.
118 changes: 43 additions & 75 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,12 @@ function get_dynamic_blocks_regex() {
* @since 1.9.0
*
* @param array $block A single parsed block object.
* @param array $inner_blocks A list of processed inner blocks.
* @return string String of rendered HTML.
*/
function gutenberg_render_block( $block ) {
function gutenberg_render_block( $block, $inner_blocks = [] ) {
$block_name = isset( $block['blockName'] ) ? $block['blockName'] : null;
$attributes = is_array( $block['attrs'] ) ? $block['attrs'] : array();
$raw_content = isset( $block['innerHTML'] ) ? $block['innerHTML'] : null;

if ( $block_name ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
Expand All @@ -164,11 +164,32 @@ function gutenberg_render_block( $block ) {
}
}

if ( $raw_content ) {
return $raw_content;
if ( empty( $block['blockMarkers' ] ) ) {
return $block['innerHTML'];
}

return '';
$output = '';
$index = 0;
foreach( $block['blockMarkers'] as $i => $p ) {
$output .= substr( $block['innerHTML'], $index, $p - $index );
$output .= $inner_blocks[ $i ];
$index = $p;
}
$output .= substr( $block['innerHTML'], $index );

return $output;
}

if ( ! class_exists( 'BlockRecursiveIteratorFilter' ) ) {
class BlockRecursiveIteratorFilter extends RecursiveFilterIterator {
public function accept() {
return is_array( $this->current() );
}

public function getChildren() {
return new self( new RecursiveArrayIterator( $this->current()['innerBlocks'] ) );
}
}
}

if ( ! function_exists( 'do_blocks' ) ) {
Expand All @@ -183,84 +204,31 @@ function gutenberg_render_block( $block ) {
*/
function do_blocks( $content ) {
global $post;

$rendered_content = '';
$dynamic_block_pattern = get_dynamic_blocks_regex();

/*
* Back up global post, to restore after render callback.
* Allows callbacks to run new WP_Query instances without breaking the global post.
*/
$global_post = $post;

while ( preg_match( $dynamic_block_pattern, $content, $block_match, PREG_OFFSET_CAPTURE ) ) {
$opening_tag = $block_match[0][0];
$offset = $block_match[0][1];
$block_name = $block_match[1][0];
$is_self_closing = isset( $block_match[4] );

// Reset attributes JSON to prevent scope bleed from last iteration.
$block_attributes_json = null;
if ( isset( $block_match[3] ) ) {
$block_attributes_json = $block_match[3][0];
$blocks = gutenberg_parse_blocks( $content );
$rai = new RecursiveArrayIterator( $blocks );
$rfi = new BlockRecursiveIteratorFilter( $rai );
$rii = new RecursiveIteratorIterator( $rfi, RecursiveIteratorIterator::CHILD_FIRST );
$stack = array();

foreach ( $rii as $block ) {
if ( ! empty( $block['innerBlocks'] ) ) {
$inner_blocks = array_splice( $stack, -1 * count( $block['innerBlocks'] ) );
$stack[] = gutenberg_render_block( $block, $inner_blocks );
} else {
$stack[] = gutenberg_render_block( $block );
}

// Since content is a working copy since the last match, append to
// rendered content up to the matched offset...
$rendered_content .= substr( $content, 0, $offset );

// ...then update the working copy of content.
$content = substr( $content, $offset + strlen( $opening_tag ) );

// Make implicit core namespace explicit.
$is_implicit_core_namespace = ( false === strpos( $block_name, '/' ) );
$normalized_block_name = $is_implicit_core_namespace ? 'core/' . $block_name : $block_name;

// Find registered block type. We can assume it exists since we use the
// `get_dynamic_block_names` function as a source for pattern matching.
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $normalized_block_name );

// Attempt to parse attributes JSON, if available.
$attributes = array();
if ( ! empty( $block_attributes_json ) ) {
$decoded_attributes = json_decode( $block_attributes_json, true );
if ( ! is_null( $decoded_attributes ) ) {
$attributes = $decoded_attributes;
}
}

$inner_content = '';

if ( ! $is_self_closing ) {
$end_tag_pattern = '/<!--\s+\/wp:' . str_replace( '/', '\/', preg_quote( $block_name ) ) . '\s+-->/';
if ( ! preg_match( $end_tag_pattern, $content, $block_match_end, PREG_OFFSET_CAPTURE ) ) {
// If no closing tag is found, abort all matching, and continue
// to append remainder of content to rendered output.
break;
}

// Update content to omit text up to and including closing tag.
$end_tag = $block_match_end[0][0];
$end_offset = $block_match_end[0][1];

$inner_content = substr( $content, 0, $end_offset );
$content = substr( $content, $end_offset + strlen( $end_tag ) );
}

// Replace dynamic block with server-rendered output.
$rendered_content .= $block_type->render( $attributes, $inner_content );

// Restore global $post.
$post = $global_post;
}

// Append remaining unmatched content.
$rendered_content .= $content;

// Strip remaining block comment demarcations.
$rendered_content = preg_replace( '/<!--\s+\/?wp:.*?-->\r?\n?/m', '', $rendered_content );
$output = '';
foreach( $stack as $block ) {
$output .= $block;
}

return $rendered_content;
return $output;
}

add_filter( 'the_content', 'do_blocks', 7 ); // BEFORE do_shortcode() and oembed.
Expand Down
2 changes: 1 addition & 1 deletion packages/block-serialization-default-parser/parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ function add_inner_block( WP_Block_Parser_Block $block, $token_start, $token_len
? $parent->block->blockMarkers[ count( $parent->block->blockMarkers ) - 1 ]
: 0;

$parent->block->innerBlocks[] = $block;
$parent->block->innerBlocks[] = (array) $block;
$parent->block->blockMarkers[] = $prev_length + self::ucs2length( $next_html );
$parent->block->innerHTML .= $next_html;
$parent->prev_offset = $last_offset ? $last_offset : $token_start + $token_length;
Expand Down

0 comments on commit 4a4feba

Please sign in to comment.