Skip to content
Open
69 changes: 37 additions & 32 deletions src/wp-content/themes/twentytwentyone/inc/template-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,44 +361,49 @@ function twenty_twenty_one_get_non_latin_css( $type = 'front-end' ) {
* @return bool Returns true if a block was located & printed, otherwise false.
*/
function twenty_twenty_one_print_first_instance_of_block( $block_name, $content = null, $instances = 1 ) {
$instances_count = 0;
$blocks_content = '';
// Scan for blocks whose block type matches the prefix, if provided a wildcard.
$prefix = rtrim( $block_name, '*' );
$match_fully = $prefix === $block_name;
$blocks_content = '';
$instance_count = 0;

if ( ! $content ) {
$content = get_the_content();
}

// Parse blocks in the content.
$blocks = parse_blocks( $content );

// Loop blocks.
foreach ( $blocks as $block ) {

// Confidence check.
if ( ! isset( $block['blockName'] ) ) {
continue;
}

// Check if this the block matches the $block_name.
$is_matching_block = false;

// If the block ends with *, try to match the first portion.
if ( '*' === $block_name[-1] ) {
$is_matching_block = 0 === strpos( $block['blockName'], rtrim( $block_name, '*' ) );
} else {
$is_matching_block = $block_name === $block['blockName'];
// Loop over top-level blocks.
if ( class_exists( '\WP_Block_Processor' ) ) {
$processor = new WP_Block_Processor( $content );

while ( $instance_count < $instances && $processor->next_block() ) {
if (
1 === $processor->get_depth() &&
/*
* Prefix matches with a wildcard require printing the block name,
* while full block-type matching can be delegated to the processor.
* In each case, the condition only holds when the match is successful.
*/
$match_fully
? $processor->is_block_type( $block_name )
: str_starts_with( $processor->get_printable_block_type(), $prefix )
) {
$blocks_content .= render_block( $processor->extract_full_block_and_advance() );
++$instance_count;
}
}

if ( $is_matching_block ) {
// Increment count.
++$instances_count;

// Add the block HTML.
$blocks_content .= render_block( $block );

// Break the loop if the $instances count was reached.
if ( $instances_count >= $instances ) {
break;
} else {
// Parse blocks in the content.
$blocks = parse_blocks( $content );

// Loop top-level blocks, releasing them from memory as soon as possible.
while ( $instance_count < $instances && null !== ( $block = array_shift( $blocks ) ) ) {
if (
$match_fully
? $block_name === $block['blockName']
: str_starts_with( $block['blockName'] ?? '', $prefix )
Copy link
Copy Markdown

@sabernhardt sabernhardt Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str_starts_with theoretically could be a problem in the legacy fallback if the site runs:

(I do not recommend editing the legacy code because then you need to test it with legacy systems.)

Any site that supports WP_Block_Processor should support str_starts_with too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in 99968c4 I removed str_starts_with() because it’s easy enough to avoid it. theoretically it would be possible that someone manually adds the WP_Block_Processor class on an old install without it.

in the process I discovered a bug I had overlooked with precedence in the conditional. this has been resolved by splitting the conditional into two.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and in 52e0dd3 I added it back, but added the feature gate. after undoing the legacy changes it didn’t seem as important to keep things coherent between them. I also misunderstood your comment initially because I overlooked that I introduced str_starts_with() in the legacy updates.

) {
$blocks_content .= render_block( $block );
++$instance_count;
}
}
}
Expand Down
Loading