Skip to content

Commit

Permalink
WIP: Parser: Add filter to process blocks after parsing
Browse files Browse the repository at this point in the history
There are numerous needs to process posts and block content from its
structured form without demanding that plugin authors implement their
own parsing systems.

Since the new default parser was implemented in #8083 the server-side
parse is now fast enough to consider doing full parses of our documents
and with that brings the idea that we can filter block content from the
parser itself.

In this patch I'm exploring an API to allow extending the parser's
behavior by post-processing blocks as they enter the parser's output
array. This new filter gives the ability to transform all of the block's
properties as they finish parsing.

In the case of inner blocks the filter runs as the inner blocks have
finished their own nesting. In the case of top-level blocks the filter
runs after all inner content has finished parsing.

One use case is in #8760 where we want to replace the HTML parts of
blocks while preserving other structure. Another use case could be
removing specific inner blocks or content based on the current user
requesting a post.

This filter exposes a kind of visitor pattern for the nested parse.

> **THIS IS AN INCOMPLETE PATCH DO NOT MERGE**
  • Loading branch information
dmsnell committed Sep 22, 2018
1 parent 01bb2f2 commit 0164e5a
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 23 deletions.
33 changes: 24 additions & 9 deletions packages/block-serialization-default-parser/parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,17 +244,19 @@ function proceed() {
*/
if ( 0 === $stack_depth ) {
if ( isset( $leading_html_start ) ) {
$this->output[] = array(
'attrs' => array(),
'innerHTML' => substr(
$this->document,
$leading_html_start,
$start_offset - $leading_html_start
),
);
$block = array(
'attrs' => array(),
'innerHTML' => substr(
$this->document,
$leading_html_start,
$start_offset - $leading_html_start
),
);

$this->output[] = self::filter_block( $block );
}

$this->output[] = new WP_Block_Parser_Block( $block_name, $attrs, array(), '' );
$this->output[] = self::filter_block( new WP_Block_Parser_Block( $block_name, $attrs, array(), '' ) );
$this->offset = $start_offset + $token_length;
return true;
}
Expand Down Expand Up @@ -458,4 +460,17 @@ function add_block_from_stack( $end_offset = null ) {

$this->output[] = $stack_top->block;
}

static function filter_block( &$block ) {
if ( ! function_exists( 'apply_filters' ) ) {
return $block;
}

/**
* Filter to allow plugins to process blocks after parsing
*
* @since 4.0.0
*/
return apply_filters( 'block_post_parse', $block );
}
}
28 changes: 20 additions & 8 deletions packages/block-serialization-default-parser/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ function Frame( block, tokenStart, tokenLength, prevOffset, leadingHtmlStart ) {
};
}

function filterBlock( block ) {
if (
'undefined' === typeof wp ||
'undefined' === typeof wp.hooks ||
'function' !== typeof wp.hooks.applyFilters
) {
return block;
}

return wp.hooks.applyFilters( 'blocks.postParse', block );
}

export const parse = ( doc ) => {
document = doc;
offset = 0;
Expand Down Expand Up @@ -78,12 +90,12 @@ function proceed() {
// in the top-level of the document
if ( 0 === stackDepth ) {
if ( null !== leadingHtmlStart ) {
output.push( {
output.push( filterBlock( {
attrs: {},
innerHTML: document.substr( leadingHtmlStart, startOffset - leadingHtmlStart ),
} );
} ) );
}
output.push( Block( blockName, attrs, [], '' ) );
output.push( filterBlock( Block( blockName, attrs, [], '' ) ) );
offset = startOffset + tokenLength;
return true;
}
Expand Down Expand Up @@ -227,10 +239,10 @@ function addFreeform( rawLength ) {
// specifies an object that's different. we can update the
// specification and change here if we want to but for now we
// want this parser to be spec-compliant
output.push( {
output.push( filterBlock( {
attrs: {},
innerHTML: document.substr( offset, length ),
} );
} ) );
}

function addInnerBlock( block, tokenStart, tokenLength, lastOffset ) {
Expand All @@ -253,11 +265,11 @@ function addBlockFromStack( endOffset ) {
}

if ( null !== leadingHtmlStart ) {
output.push( {
output.push( filterBlock( {
attrs: {},
innerHTML: document.substr( leadingHtmlStart, tokenStart - leadingHtmlStart ),
} );
} ) );
}

output.push( block );
output.push( filterBlock( block ) );
}
43 changes: 37 additions & 6 deletions packages/block-serialization-spec-parser/grammar.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,21 @@ if ( ! function_exists( 'peg_join_blocks' ) ) {
}
}
if ( ! function_exists( 'peg_filter_block' ) ) {
function peg_filter_block( &$block ) {
if ( ! function_exists( 'apply_filters' ) ) {
return $block;
}
/**
* Filter to allow plugins to process blocks after parsing
*
* @since 4.0.0
*/
return apply_filters( 'block_post_parse', $block );
}
}

?> **/

function freeform( s ) {
Expand Down Expand Up @@ -152,6 +167,18 @@ function partition( predicate, list ) {
return [ truthy, falsey ];
}

function filterBlock( block ) {
if (
'undefined' === typeof wp ||
'undefined' === typeof wp.hooks ||
'function' !== typeof wp.hooks.applyFilters
) {
return block;
}

return wp.hooks.applyFilters( 'blocks.postParse', block );
}

}

//////////////////////////////////////////////////////
Expand Down Expand Up @@ -179,20 +206,22 @@ Block_Void
})? "/-->"
{
/** <?php
return array(
$block = array(
'blockName' => $blockName,
'attrs' => $attrs,
'innerBlocks' => array(),
'innerHTML' => '',
);
return peg_filter_block( $block );
?> **/

return {
return filterBlock( {
blockName: blockName,
attrs: attrs,
innerBlocks: [],
innerHTML: ''
};
} );
}

Block_Balanced
Expand All @@ -201,24 +230,26 @@ Block_Balanced
/** <?php
list( $innerHTML, $innerBlocks ) = peg_array_partition( $children, 'is_string' );
return array(
$block = array(
'blockName' => $s['blockName'],
'attrs' => $s['attrs'],
'innerBlocks' => $innerBlocks,
'innerHTML' => implode( '', $innerHTML ),
);
return peg_filter_block( $block );
?> **/

var innerContent = partition( function( a ) { return 'string' === typeof a }, children );
var innerHTML = innerContent[ 0 ];
var innerBlocks = innerContent[ 1 ];

return {
return filterBlock( {
blockName: s.blockName,
attrs: s.attrs,
innerBlocks: innerBlocks,
innerHTML: innerHTML.join( '' )
};
} );
}

Block_Start
Expand Down

0 comments on commit 0164e5a

Please sign in to comment.