Skip to content
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

Performance: Try refactoring get_hooked_blocks() to check impact #5399

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/wp-includes/block-template-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -549,10 +549,15 @@ function _build_block_template_result_from_file( $template_file, $template_type
$template->area = $template_file['area'];
}

$blocks = parse_blocks( $template_content );
$before_block_visitor = make_before_block_visitor( $template );
$after_block_visitor = make_after_block_visitor( $template );
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
$before_block_visitor = '_inject_theme_attribute_in_template_part_block';
$after_block_visitor = null;
$hooked_blocks = get_hooked_blocks();
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;
}
Expand Down
54 changes: 26 additions & 28 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -723,30 +723,30 @@ 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
*
* @param string $name Block type name including namespace.
* @return array[] Array of block types grouped by their relative position.
* @return array[] Array of block types grouped by anchor block type and the relative position.
*/
function get_hooked_blocks( $name ) {
function get_hooked_blocks() {
$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
$hooked_blocks = array();
foreach ( $block_types as $block_type ) {
if ( ! ( $block_type instanceof WP_Block_Type ) || ! is_array( $block_type->block_hooks ) ) {
continue;
}
foreach ( $block_type->block_hooks as $anchor_block_type => $relative_position ) {
if ( $anchor_block_type !== $name ) {
continue;
if ( ! isset( $hooked_blocks[ $anchor_block_type ] ) ) {
$hooked_blocks[ $anchor_block_type ] = array();
}
if ( ! isset( $hooked_blocks[ $relative_position ] ) ) {
$hooked_blocks[ $relative_position ] = array();
if ( ! isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] ) ) {
$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;
}

Expand All @@ -760,11 +760,12 @@ function get_hooked_blocks( $name ) {
* @since 6.4.0
* @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,
* 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.
*
Expand All @@ -777,7 +778,7 @@ function make_before_block_visitor( $context ) {
* @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 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 );

$markup = '';
Expand All @@ -786,9 +787,8 @@ function make_before_block_visitor( $context ) {
// Candidate for first-child insertion.
$relative_position = 'first_child';
$anchor_block_type = $parent_block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type );
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] )
? $hooked_block_types[ $relative_position ]
$hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
: array();

/**
Expand All @@ -810,9 +810,8 @@ function make_before_block_visitor( $context ) {

$relative_position = 'before';
$anchor_block_type = $block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type );
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] )
? $hooked_block_types[ $relative_position ]
$hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
: array();

/** This filter is documented in wp-includes/blocks.php */
Expand All @@ -835,11 +834,12 @@ function make_before_block_visitor( $context ) {
* @since 6.4.0
* @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,
* 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.
*
Expand All @@ -851,15 +851,14 @@ function make_after_block_visitor( $context ) {
* @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 function ( &$block, $parent_block = null, $next = null ) use ( $context ) {
return function ( &$block, $parent_block = null, $next = null ) use ( $hooked_blocks, $context ) {
$markup = '';

$relative_position = 'after';
$anchor_block_type = $block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type );
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] )
? $hooked_block_types[ $relative_position ]
: array();
$hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
: array();

/** 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 );
Expand All @@ -871,9 +870,8 @@ function make_after_block_visitor( $context ) {
// Candidate for last-child insertion.
$relative_position = 'last_child';
$anchor_block_type = $parent_block['blockName'];
$hooked_block_types = get_hooked_blocks( $anchor_block_type );
$hooked_block_types = isset( $hooked_block_types[ $relative_position ] )
? $hooked_block_types[ $relative_position ]
$hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
: array();

/** This filter is documented in wp-includes/blocks.php */
Expand Down
38 changes: 27 additions & 11 deletions src/wp-includes/class-wp-block-patterns-registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,28 @@ public function unregister( $pattern_name ) {
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.
*
Expand All @@ -165,11 +187,8 @@ public function get_registered( $pattern_name ) {
return null;
}

$pattern = $this->registered_patterns[ $pattern_name ];
$blocks = parse_blocks( $pattern['content'] );
$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 );
$pattern = $this->registered_patterns[ $pattern_name ];
$pattern['content'] = $this->prepare_content( $pattern, get_hooked_blocks() );

return $pattern;
}
Expand All @@ -184,17 +203,14 @@ public function get_registered( $pattern_name ) {
* and per style.
*/
public function get_all_registered( $outside_init_only = false ) {
$patterns = array_values(
$patterns = array_values(
$outside_init_only
? $this->registered_patterns_outside_init
: $this->registered_patterns
);

$hooked_blocks = get_hooked_blocks();
foreach ( $patterns as $index => $pattern ) {
$blocks = parse_blocks( $pattern['content'] );
$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 );
$patterns[ $index ]['content'] = $this->prepare_content( $pattern, $hooked_blocks );
}
return $patterns;
}
Expand Down
69 changes: 27 additions & 42 deletions tests/phpunit/tests/blocks/getHookedBlocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private function switch_to_block_theme_hooked_blocks() {
* @covers ::get_hooked_blocks
*/
public function test_get_hooked_blocks_no_match_found() {
$result = get_hooked_blocks( 'tests/no-hooked-blocks' );
$result = get_hooked_blocks();

$this->assertSame( array(), $result );
}
Expand Down Expand Up @@ -98,53 +98,38 @@ public function test_get_hooked_blocks_matches_found() {

$this->assertSame(
array(
'before' => array(
'tests/injected-one',
'tests/injected-two',
'tests/hooked-at-before' => array(
'before' => array(
'tests/injected-one',
'tests/injected-two',
),
),
),
get_hooked_blocks( 'tests/hooked-at-before' ),
'block hooked at the before position'
);
$this->assertSame(
array(
'after' => array(
'tests/injected-one',
'tests/injected-two',
'tests/hooked-at-after' => array(
'after' => array(
'tests/injected-one',
'tests/injected-two',
),
),
),
get_hooked_blocks( 'tests/hooked-at-after' ),
'block hooked at the after position'
);
$this->assertSame(
array(
'first_child' => array(
'tests/injected-two',
'tests/hooked-at-before-and-after' => array(
'before' => array(
'tests/injected-one',
),
'after' => array(
'tests/injected-two',
),
),
),
get_hooked_blocks( 'tests/hooked-at-first-child' ),
'block hooked at the first child position'
);
$this->assertSame(
array(
'last_child' => array(
'tests/injected-two',
),
),
get_hooked_blocks( 'tests/hooked-at-last-child' ),
'block hooked at the last child position'
);
$this->assertSame(
array(
'before' => array(
'tests/injected-one',
'tests/hooked-at-first-child' => array(
'first_child' => array(
'tests/injected-two',
),
),
'after' => array(
'tests/injected-two',
'tests/hooked-at-last-child' => array(
'last_child' => array(
'tests/injected-two',
),
),
),
get_hooked_blocks( 'tests/hooked-at-before-and-after' ),
'block hooked before one block and after another'
get_hooked_blocks()
);
}

Expand Down