-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Backport: Templates perf: resolve patterns server side #6673
Changes from all commits
def1b1f
24abdb6
259aa27
9a064d9
11afd37
a884258
8a8c841
c823184
3938a2f
85fddc4
c16e3e2
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 | ||||
---|---|---|---|---|---|---|
|
@@ -1344,6 +1344,83 @@ function traverse_and_serialize_block( $block, $pre_callback = null, $post_callb | |||||
); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Replaces patterns in a block tree with their content. | ||||||
* | ||||||
* @since 6.6.0 | ||||||
* | ||||||
* @param array $blocks An array blocks. | ||||||
* | ||||||
* @return array An array of blocks with patterns replaced by their content. | ||||||
*/ | ||||||
function resolve_pattern_blocks( $blocks ) { | ||||||
static $inner_content; | ||||||
// Keep track of seen references to avoid infinite loops. | ||||||
static $seen_refs = array(); | ||||||
$i = 0; | ||||||
while ( $i < count( $blocks ) ) { | ||||||
if ( 'core/pattern' === $blocks[ $i ]['blockName'] ) { | ||||||
$attrs = $blocks[ $i ]['attrs']; | ||||||
|
||||||
if ( empty( $attrs['slug'] ) ) { | ||||||
++$i; | ||||||
continue; | ||||||
} | ||||||
|
||||||
$slug = $attrs['slug']; | ||||||
|
||||||
if ( isset( $seen_refs[ $slug ] ) ) { | ||||||
// Skip recursive patterns. | ||||||
array_splice( $blocks, $i, 1 ); | ||||||
continue; | ||||||
} | ||||||
|
||||||
$registry = WP_Block_Patterns_Registry::get_instance(); | ||||||
$pattern = $registry->get_registered( $slug ); | ||||||
|
||||||
// Skip unknown patterns. | ||||||
if ( ! $pattern ) { | ||||||
++$i; | ||||||
continue; | ||||||
} | ||||||
|
||||||
$blocks_to_insert = parse_blocks( $pattern['content'] ); | ||||||
$seen_refs[ $slug ] = true; | ||||||
$prev_inner_content = $inner_content; | ||||||
$inner_content = null; | ||||||
$blocks_to_insert = resolve_pattern_blocks( $blocks_to_insert ); | ||||||
$inner_content = $prev_inner_content; | ||||||
unset( $seen_refs[ $slug ] ); | ||||||
array_splice( $blocks, $i, 1, $blocks_to_insert ); | ||||||
|
||||||
// If we have inner content, we need to insert nulls in the | ||||||
// inner content array, otherwise serialize_blocks will skip | ||||||
// blocks. | ||||||
if ( $inner_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. If
Suggested change
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 always an array, yes, there's also an empty check below. Also this argument is meant to be private, not sure if there's a better way to do it. |
||||||
$null_indices = array_keys( $inner_content, null, true ); | ||||||
$content_index = $null_indices[ $i ]; | ||||||
$nulls = array_fill( 0, count( $blocks_to_insert ), null ); | ||||||
array_splice( $inner_content, $content_index, 1, $nulls ); | ||||||
} | ||||||
|
||||||
// Skip inserted blocks. | ||||||
$i += count( $blocks_to_insert ); | ||||||
} else { | ||||||
if ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) { | ||||||
$prev_inner_content = $inner_content; | ||||||
$inner_content = $blocks[ $i ]['innerContent']; | ||||||
$blocks[ $i ]['innerBlocks'] = resolve_pattern_blocks( | ||||||
$blocks[ $i ]['innerBlocks'] | ||||||
); | ||||||
$blocks[ $i ]['innerContent'] = $inner_content; | ||||||
$inner_content = $prev_inner_content; | ||||||
} | ||||||
++$i; | ||||||
} | ||||||
} | ||||||
return $blocks; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Given an array of parsed block trees, applies callbacks before and after serializing them and | ||||||
* returns their concatenated output. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<?php | ||
/** | ||
* Tests for resolve_pattern_blocks. | ||
* | ||
* @package WordPress | ||
* @subpackage Blocks | ||
* | ||
* @since 6.6.0 | ||
* | ||
* @group blocks | ||
* @covers resolve_pattern_blocks | ||
*/ | ||
class Tests_Blocks_ResolvePatternBlocks extends WP_UnitTestCase { | ||
public function set_up() { | ||
parent::set_up(); | ||
|
||
register_block_pattern( | ||
'core/test', | ||
array( | ||
'title' => 'Test', | ||
'content' => '<!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph -->', | ||
'description' => 'Test pattern.', | ||
) | ||
); | ||
register_block_pattern( | ||
'core/recursive', | ||
array( | ||
'title' => 'Recursive', | ||
'content' => '<!-- wp:paragraph -->Recursive<!-- /wp:paragraph --><!-- wp:pattern {"slug":"core/recursive"} /-->', | ||
'description' => 'Recursive pattern.', | ||
) | ||
); | ||
} | ||
|
||
public function tear_down() { | ||
unregister_block_pattern( 'core/test' ); | ||
unregister_block_pattern( 'core/recursive' ); | ||
|
||
parent::tear_down(); | ||
} | ||
|
||
/** | ||
* @dataProvider data_should_resolve_pattern_blocks_as_expected | ||
* | ||
* @ticket 61228 | ||
* | ||
* @param string $blocks A string representing blocks that need resolving. | ||
* @param string $expected Expected result. | ||
*/ | ||
public function test_should_resolve_pattern_blocks_as_expected( $blocks, $expected ) { | ||
$actual = resolve_pattern_blocks( parse_blocks( $blocks ) ); | ||
$this->assertSame( $expected, serialize_blocks( $actual ) ); | ||
} | ||
|
||
/** | ||
* Data provider. | ||
* | ||
* @return array | ||
*/ | ||
public function data_should_resolve_pattern_blocks_as_expected() { | ||
return array( | ||
// Works without attributes, leaves the block as is. | ||
'pattern with no slug attribute' => array( '<!-- wp:pattern /-->', '<!-- wp:pattern /-->' ), | ||
// Resolves the pattern. | ||
'test pattern' => array( '<!-- wp:pattern {"slug":"core/test"} /-->', '<!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph -->' ), | ||
// Skips recursive patterns. | ||
'recursive pattern' => array( '<!-- wp:pattern {"slug":"core/recursive"} /-->', '<!-- wp:paragraph -->Recursive<!-- /wp:paragraph -->' ), | ||
// Resolves the pattern within a block. | ||
'pattern within a block' => array( '<!-- wp:group --><!-- wp:paragraph -->Before<!-- /wp:paragraph --><!-- wp:pattern {"slug":"core/test"} /--><!-- wp:paragraph -->After<!-- /wp:paragraph --><!-- /wp:group -->', '<!-- wp:group --><!-- wp:paragraph -->Before<!-- /wp:paragraph --><!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph --><!-- wp:paragraph -->After<!-- /wp:paragraph --><!-- /wp:group -->' ), | ||
); | ||
} | ||
} |
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.
From what I know, there’s no need to add an empty line between the
@param
and@return
tags. Also, I noticed the description for the@inner_content
function argument is missing. Could that be added?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 is meant to be a private arg, not sure if there's a better way https://github.com/WordPress/wordpress-develop/pull/6673/files#r1623854851
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.
As far as I know, there is no concept of a private argument in PHP. I'm also unsure how common it is to omit a function argument from a DocBlock if it is not intended for use.
However, there might be a solution to eliminate the need for the
$inner_content
function argument:Probably not optimal, but it passes the tests.
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.
Tbh, I find it a bit more confusing. Also, why is it now sometimes returning a blocks array and sometimes one block?
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.
I feel the same way.
I made this change to eliminate the
$inner_content
parameter, aiming to streamline the function's interface.I've tried to refactor this function into two separate functions, but it didn't go well either. This likely indicates that the current structure might be the most efficient approach.