Blocks: Call get_hooked_blocks only once per template/part/pattern.

Prior to this changeset, `get_hooked_blocks` was called four times ''for every parsed block'' in each template, template part, and pattern. With this changeset applied, `get_hooked_blocks` is called only once per template, template part, or pattern.

Additionally, `get_hooked_blocks` is called only once when returning the list of all registered patterns. (The latter modification brings the implementation closer to its state prior to Block Hooks.)

Finally, when there are no registered hooked blocks or `hooked_block_types` filters, parsing, hooked block insertion, and re-serializing is skipped altogether.

Props gziolo, flixos90, joemcgill, dmsnell, spacedmonkey, hellofromtonya.
Fixes .
Built from https://develop.svn.wordpress.org/trunk@56805


git-svn-id: http://core.svn.wordpress.org/trunk@56317 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Bernhard Reiter 2023-10-09 16:40:24 +00:00
parent e0596b5835
commit fc229aa436
4 changed files with 63 additions and 44 deletions

@ -549,10 +549,15 @@ function _build_block_template_result_from_file( $template_file, $template_type
$template->area = $template_file['area']; $template->area = $template_file['area'];
} }
$blocks = parse_blocks( $template_content ); $before_block_visitor = '_inject_theme_attribute_in_template_part_block';
$before_block_visitor = make_before_block_visitor( $template ); $after_block_visitor = null;
$after_block_visitor = make_after_block_visitor( $template ); $hooked_blocks = get_hooked_blocks();
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
}
$blocks = parse_blocks( $template_content );
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
return $template; return $template;
} }

@ -723,14 +723,13 @@ function get_dynamic_block_names() {
} }
/** /**
* Retrieves block types hooked into the given block, grouped by their relative position. * Retrieves block types hooked into the given block, grouped by anchor block type and the relative position.
* *
* @since 6.4.0 * @since 6.4.0
* *
* @param string $name Block type name including namespace. * @return array[] Array of block types grouped by anchor block type and the relative position.
* @return array[] Array of block types grouped by their relative position.
*/ */
function get_hooked_blocks( $name ) { function get_hooked_blocks() {
$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
$hooked_blocks = array(); $hooked_blocks = array();
foreach ( $block_types as $block_type ) { foreach ( $block_types as $block_type ) {
@ -738,15 +737,16 @@ function get_hooked_blocks( $name ) {
continue; continue;
} }
foreach ( $block_type->block_hooks as $anchor_block_type => $relative_position ) { foreach ( $block_type->block_hooks as $anchor_block_type => $relative_position ) {
if ( $anchor_block_type !== $name ) { if ( ! isset( $hooked_blocks[ $anchor_block_type ] ) ) {
continue; $hooked_blocks[ $anchor_block_type ] = array();
} }
if ( ! isset( $hooked_blocks[ $relative_position ] ) ) { if ( ! isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] ) ) {
$hooked_blocks[ $relative_position ] = array(); $hooked_blocks[ $anchor_block_type ][ $relative_position ] = array();
} }
$hooked_blocks[ $relative_position ][] = $block_type->name; $hooked_blocks[ $anchor_block_type ][ $relative_position ][] = $block_type->name;
} }
} }
return $hooked_blocks; return $hooked_blocks;
} }
@ -760,11 +760,12 @@ function get_hooked_blocks( $name ) {
* @since 6.4.0 * @since 6.4.0
* @access private * @access private
* *
* @param WP_Block_Template|array $context A block template, template part, or pattern that the blocks belong to. * @param array $hooked_blocks An array of blocks hooked to another given block.
* @param WP_Block_Template|array $context A block template, template part, or pattern that the blocks belong to.
* @return callable A function that returns the serialized markup for the given block, * @return callable A function that returns the serialized markup for the given block,
* including the markup for any hooked blocks before it. * including the markup for any hooked blocks before it.
*/ */
function make_before_block_visitor( $context ) { function make_before_block_visitor( $hooked_blocks, $context ) {
/** /**
* Injects hooked blocks before the given block, injects the `theme` attribute into Template Part blocks, and returns the serialized markup. * Injects hooked blocks before the given block, injects the `theme` attribute into Template Part blocks, and returns the serialized markup.
* *
@ -777,7 +778,7 @@ function make_before_block_visitor( $context ) {
* @param array $prev The previous sibling block of the given block. * @param array $prev The previous sibling block of the given block.
* @return string The serialized markup for the given block, with the markup for any hooked blocks prepended to it. * @return string The serialized markup for the given block, with the markup for any hooked blocks prepended to it.
*/ */
return function ( &$block, $parent_block = null, $prev = null ) use ( $context ) { return function ( &$block, $parent_block = null, $prev = null ) use ( $hooked_blocks, $context ) {
_inject_theme_attribute_in_template_part_block( $block ); _inject_theme_attribute_in_template_part_block( $block );
$markup = ''; $markup = '';
@ -786,9 +787,8 @@ function make_before_block_visitor( $context ) {
// Candidate for first-child insertion. // Candidate for first-child insertion.
$relative_position = 'first_child'; $relative_position = 'first_child';
$anchor_block_type = $parent_block['blockName']; $anchor_block_type = $parent_block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type ); $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] ) ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
? $hooked_block_types[ $relative_position ]
: array(); : array();
/** /**
@ -810,9 +810,8 @@ function make_before_block_visitor( $context ) {
$relative_position = 'before'; $relative_position = 'before';
$anchor_block_type = $block['blockName']; $anchor_block_type = $block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type ); $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] ) ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
? $hooked_block_types[ $relative_position ]
: array(); : array();
/** This filter is documented in wp-includes/blocks.php */ /** This filter is documented in wp-includes/blocks.php */
@ -835,11 +834,12 @@ function make_before_block_visitor( $context ) {
* @since 6.4.0 * @since 6.4.0
* @access private * @access private
* *
* @param WP_Block_Template|array $context A block template, template part, or pattern that the blocks belong to. * @param array $hooked_blocks An array of blocks hooked to another block.
* @param WP_Block_Template|array $context A block template, template part, or pattern that the blocks belong to.
* @return callable A function that returns the serialized markup for the given block, * @return callable A function that returns the serialized markup for the given block,
* including the markup for any hooked blocks after it. * including the markup for any hooked blocks after it.
*/ */
function make_after_block_visitor( $context ) { function make_after_block_visitor( $hooked_blocks, $context ) {
/** /**
* Injects hooked blocks after the given block, and returns the serialized markup. * Injects hooked blocks after the given block, and returns the serialized markup.
* *
@ -851,15 +851,14 @@ function make_after_block_visitor( $context ) {
* @param array $next The next sibling block of the given block. * @param array $next The next sibling block of the given block.
* @return string The serialized markup for the given block, with the markup for any hooked blocks appended to it. * @return string The serialized markup for the given block, with the markup for any hooked blocks appended to it.
*/ */
return function ( &$block, $parent_block = null, $next = null ) use ( $context ) { return function ( &$block, $parent_block = null, $next = null ) use ( $hooked_blocks, $context ) {
$markup = ''; $markup = '';
$relative_position = 'after'; $relative_position = 'after';
$anchor_block_type = $block['blockName']; $anchor_block_type = $block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type ); $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] ) ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
? $hooked_block_types[ $relative_position ] : array();
: array();
/** This filter is documented in wp-includes/blocks.php */ /** This filter is documented in wp-includes/blocks.php */
$hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
@ -871,9 +870,8 @@ function make_after_block_visitor( $context ) {
// Candidate for last-child insertion. // Candidate for last-child insertion.
$relative_position = 'last_child'; $relative_position = 'last_child';
$anchor_block_type = $parent_block['blockName']; $anchor_block_type = $parent_block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type ); $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] ) ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
? $hooked_block_types[ $relative_position ]
: array(); : array();
/** This filter is documented in wp-includes/blocks.php */ /** This filter is documented in wp-includes/blocks.php */

@ -152,6 +152,28 @@ final class WP_Block_Patterns_Registry {
return true; return true;
} }
/**
* Prepares the content of a block pattern. If hooked blocks are registered, they get injected into the pattern,
* when they met the defined criteria.
*
* @since 6.4.0
*
* @param array $pattern Registered pattern properties.
* @param array $hooked_blocks The list of hooked blocks.
* @return string The content of the block pattern.
*/
private function prepare_content( $pattern, $hooked_blocks ) {
$content = $pattern['content'];
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$blocks = parse_blocks( $content );
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $pattern );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $pattern );
$content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
}
return $content;
}
/** /**
* Retrieves an array containing the properties of a registered block pattern. * Retrieves an array containing the properties of a registered block pattern.
* *
@ -165,11 +187,8 @@ final class WP_Block_Patterns_Registry {
return null; return null;
} }
$pattern = $this->registered_patterns[ $pattern_name ]; $pattern = $this->registered_patterns[ $pattern_name ];
$blocks = parse_blocks( $pattern['content'] ); $pattern['content'] = $this->prepare_content( $pattern, get_hooked_blocks() );
$before_block_visitor = make_before_block_visitor( $pattern );
$after_block_visitor = make_after_block_visitor( $pattern );
$pattern['content'] = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
return $pattern; return $pattern;
} }
@ -184,17 +203,14 @@ final class WP_Block_Patterns_Registry {
* and per style. * and per style.
*/ */
public function get_all_registered( $outside_init_only = false ) { public function get_all_registered( $outside_init_only = false ) {
$patterns = array_values( $patterns = array_values(
$outside_init_only $outside_init_only
? $this->registered_patterns_outside_init ? $this->registered_patterns_outside_init
: $this->registered_patterns : $this->registered_patterns
); );
$hooked_blocks = get_hooked_blocks();
foreach ( $patterns as $index => $pattern ) { foreach ( $patterns as $index => $pattern ) {
$blocks = parse_blocks( $pattern['content'] ); $patterns[ $index ]['content'] = $this->prepare_content( $pattern, $hooked_blocks );
$before_block_visitor = make_before_block_visitor( $pattern );
$after_block_visitor = make_after_block_visitor( $pattern );
$patterns[ $index ]['content'] = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
} }
return $patterns; return $patterns;
} }

@ -16,7 +16,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '6.4-beta2-56804'; $wp_version = '6.4-beta2-56805';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.