-
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
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN:
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
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.
Adding PHPUnit tests would be beneficial since just looking at the source code makes it hard to tell if this function works as intended.
By the way, I noticed that the $inner_content
function argument might not be necessary because it seems to only pass information that's already in the $blocks
function argument.
What do you think?
* @param array $blocks An array blocks. | ||
* | ||
* @return array An array of blocks with patterns replaced by their 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.
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?
* @param array $blocks An array blocks. | |
* | |
* @return array An array of blocks with patterns replaced by their content. | |
* @param array $blocks An array blocks. | |
* @param array $inner_content An optional array of inner content. | |
* @return array An array of blocks with patterns replaced by their 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.
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:
/**
* Replaces patterns in a block tree with their content.
*
* @since 6.6.0
*
* @param array $blocks An array of blocks or a parent block.
* @return array An array of blocks with patterns replaced by their content.
*/
function resolve_pattern_blocks( $blocks ) {
// Keep track of seen references to avoid infinite loops.
static $seen_refs = array();
$i = 0;
$is_parent_block = ! empty( $blocks['innerBlocks'] );
if ( $is_parent_block ) {
// If it is a single block, $blocks must be reinitialized before it can be used.
$block = $blocks;
$blocks = $block['innerBlocks'];
$inner_content = $block['innerContent'];
}
while ( $i < count( $blocks ) ) {
if ( 'core/pattern' !== $blocks[ $i ]['blockName'] ) {
if ( $is_parent_block ) {
$blocks[ $i ] = resolve_pattern_blocks( $blocks[ $i ] );
}
++$i;
continue;
}
$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;
$blocks_to_insert = resolve_pattern_blocks( $blocks_to_insert );
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 ( !empty( $inner_content ) ) {
$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 );
}
if ( $is_parent_block ) {
$block['innerBlocks'] = $blocks;
$block['innerContent'] = $inner_content;
return $block;
}
return $blocks;
}
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.
Tbh, I find it a bit more confusing.
I feel the same way.
why is it now sometimes returning a blocks array and sometimes one block?
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.
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
If @inner_content
is expected to be an array, it might be a good idea to explicitly check to ensure it is an array to avoid PHP warnings. Is there a guarantee that $blocks[$i]['innerBlocks']
is always an array?
if ( $inner_content ) { | |
if ( is_array( $inner_content ) && $inner_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.
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.
@anton-vlasenko Yes, it needs unit tests, but I'm unfamiliar with unit test in PHP and core. Would you be able to assist? |
Yes, I can look into it. |
I inline commented on that, it is necessary to change because serialise will break if it's not adjusted |
@anton-vlasenko Added unit 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.
I've been testing this PR for some time and it worked as intended. The only change I would make is to remove the else
operator, as it's not needed and allows to remove nesting. However, this is not a blocker:
function resolve_pattern_blocks( $blocks, &$inner_content = null ) {
// 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'] ) {
if ( ! empty( $blocks[ $i ]['innerBlocks'] ) ) {
$blocks[ $i ]['innerBlocks'] = resolve_pattern_blocks(
$blocks[ $i ]['innerBlocks'],
$blocks[ $i ]['innerContent']
);
}
++ $i;
continue;
}
$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;
$blocks_to_insert = resolve_pattern_blocks( $blocks_to_insert );
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 ) {
$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 );
}
return $blocks;
}
See WordPress/gutenberg#60349. See WordPress/gutenberg#61757. See #6673. Fixes #61228. Props ellatrix, antonvlasenko. git-svn-id: https://develop.svn.wordpress.org/trunk@58303 602fd350-edb4-49c9-b593-d223f7449a82
See WordPress/gutenberg#60349. See WordPress/gutenberg#61757. See WordPress/wordpress-develop#6673. Fixes #61228. Props ellatrix, antonvlasenko. Built from https://develop.svn.wordpress.org/trunk@58303 git-svn-id: http://core.svn.wordpress.org/trunk@57760 1a063a9b-81f0-0310-95a4-ce76da25c4cd
See WordPress/gutenberg#60349. See WordPress/gutenberg#61757. See WordPress/wordpress-develop#6673. Fixes #61228. Props ellatrix, antonvlasenko. Built from https://develop.svn.wordpress.org/trunk@58303 git-svn-id: https://core.svn.wordpress.org/trunk@57760 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Note that this replaces #6559, which I hadn't created from my fork.
Trac ticket: https://core.trac.wordpress.org/ticket/61228
This is a single backport for the following PRs:
To test: check the patterns endpoint and search for
wp:pattern
. There should be 0 results.This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.