-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parser: Replace dynamic-block regex in do_blocks
#10463
Changes from all commits
84552d9
d7811ca
1bc27bc
0c552f8
69725a0
d37c337
33d5234
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -110,6 +110,7 @@ function get_dynamic_block_names() { | |
* @return string | ||
*/ | ||
function get_dynamic_blocks_regex() { | ||
_deprecated_function( __FUNCTION__, '4.1.0', __( "Dynamic blocks shouldn't be extracted by regex. Use parse_blocks() instead.", 'gutenberg' ) ); | ||
$dynamic_block_names = get_dynamic_block_names(); | ||
$dynamic_block_pattern = ( | ||
'/<!--\s+wp:(' . | ||
|
@@ -171,98 +172,107 @@ function gutenberg_render_block( $block ) { | |
* @return string Updated post content. | ||
*/ | ||
function do_blocks( $content ) { | ||
global $post; | ||
$blocks = gutenberg_parse_blocks( $content ); | ||
return _recurse_blocks( $blocks, $blocks ); | ||
} | ||
add_filter( 'the_content', 'do_blocks', 9 ); // BEFORE do_shortcode(). | ||
|
||
$rendered_content = ''; | ||
$dynamic_block_pattern = get_dynamic_blocks_regex(); | ||
/** | ||
* Helper function for do_blocks(), to recurse through the block tree. | ||
* | ||
* @since 4.1.0 | ||
* @access private | ||
* | ||
* @param array $blocks Array of blocks from parse_blocks() | ||
* @return string | ||
*/ | ||
function _recurse_blocks( $blocks, $all_blocks ) { | ||
global $post; | ||
|
||
/* | ||
* 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]; | ||
$rendered_content = ''; | ||
$dynamic_blocks = get_dynamic_block_names(); | ||
|
||
foreach ( $blocks as $block ) { | ||
$block = (array) $block; | ||
if ( in_array( $block['blockName'], $dynamic_blocks ) ) { | ||
// 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( $block['blockName'] ); | ||
|
||
// Replace dynamic block with server-rendered output. | ||
$block_content = $block_type->render( (array) $block['attrs'], $block['innerHTML'] ); | ||
} elseif ( $block['innerBlocks'] ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way these conditionals are laid, blocks can either be Dynamic Blocks, OR they can have innerBlocks. It doesn't permit Dynamic Blocks to have innerBlocks. Which seems bad. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is true, hence the reason this PR exists. I'd rather not render inner blocks first then pass them up because then that limits what we can do with inner blocks. it should be good enough to give blocks their inner blocks in structural form, let them decide if they want to modify or filter them, then render the document. this PR is just one step in the process - we have lots to overhaul here and maybe I'm wrong but I thought that @mcsf decided to keep this as-is until post merge and leave dynamic blocks the way they are - as in, yesterday or the day before he decided. let's see what he says |
||
$block_content = _recurse_blocks( $block['innerBlocks'], $all_blocks ); | ||
} else { | ||
$block_content = $block['innerHTML']; | ||
} | ||
|
||
// 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 ); | ||
/** | ||
* Filters the content of a single block. | ||
* | ||
* During the_content, each block is parsed and added to the output individually. This filter allows | ||
* that content to be altered immediately before it's appended. | ||
* | ||
* @since 5.0.0 | ||
* | ||
* @param string $block_content The block content about to be appended. | ||
* @param array $block The full block, including name and attributes. | ||
* @param array $all_blocks The array of all blocks being processed. | ||
*/ | ||
$rendered_content .= apply_filters( 'do_block', $block_content, $block, $all_blocks ); | ||
|
||
// 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 ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think removing this line is causing the unit test failure in https://travis-ci.org/WordPress/gutenberg/jobs/439435606 as it's changing how some whitespace renders? The only difference is an extra line break showing up in the output, so not a huge deal, but this may be accounting for it. |
||
$rendered_content = preg_replace( '/<!--\s+\/?wp:.*?-->/m', '', $rendered_content ); | ||
|
||
return $rendered_content; | ||
} | ||
add_filter( 'the_content', 'do_blocks', 9 ); // BEFORE do_shortcode(). | ||
|
||
/** | ||
* Remove all dynamic blocks from the given content. | ||
* | ||
* @since 3.6.0 | ||
* @since 4.1.0 | ||
* | ||
* @param string $content Content of the current post. | ||
* @return string | ||
*/ | ||
function strip_dynamic_blocks( $content ) { | ||
return preg_replace( get_dynamic_blocks_regex(), '', $content ); | ||
return _recurse_strip_dynamic_blocks( gutenberg_parse_blocks( $content ) ); | ||
} | ||
|
||
/** | ||
* Helper function for strip_dynamic_blocks(), to recurse through the block tree. | ||
* | ||
* @since 4.1.0 | ||
* @access private | ||
* | ||
* @param array $blocks Array of blocks from parse_blocks() | ||
* @return string | ||
*/ | ||
function _recurse_strip_dynamic_blocks( $blocks ) { | ||
$clean_content = ''; | ||
$dynamic_blocks = get_dynamic_block_names(); | ||
|
||
foreach ( $blocks as $block ) { | ||
if ( ! in_array( $block['blockName'], $dynamic_blocks ) ) { | ||
if ( $block['innerBlocks'] ) { | ||
$clean_content .= _recurse_strip_dynamic_blocks( $block['innerBlocks'] ); | ||
} else { | ||
$clean_content .= $block['innerHTML']; | ||
} | ||
} | ||
} | ||
|
||
return $clean_content; | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just do something like this, to allow individual plugins to override as necessary for more custom functionality that Core doesn't support w/r/t content, html, inner blocks, etc?
or potentially even
apply_filters( 'pre_do_block_' . $block['blockName'], null, $block, $all_blocks )
to narrow the filter instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be taken advantage of via something like this:
Automattic/jetpack@a12356b