Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
36 changes: 32 additions & 4 deletions src/wp-includes/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
* @since 6.3.0 Adds compound class to layout wrapper for global spacing styles.
* @since 6.3.0 Check for layout support via the `layout` key with fallback to `__experimentalLayout`.
* @since 6.6.0 Removed duplicate container class from layout styles.
* @since 7.1.0 Returns early before resolving global settings.
Comment thread
ramonjd marked this conversation as resolved.
Outdated
Comment thread
ramonjd marked this conversation as resolved.
Outdated
* @access private
*
* @param string $block_content Rendered block content.
Expand All @@ -955,13 +956,40 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
function wp_render_layout_support_flag( $block_content, $block ) {
static $global_styles = null;

$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$block_supports_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
$style_attr = $block['attrs']['style'] ?? array();
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$block_supports_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
$style_attr = $block['attrs']['style'] ?? array();
// If there is any value in style -> layout, the block has a child layout.
$child_layout = $style_attr['layout'] ?? null;

/*
* Responsive child layouts are nested under a style state key such as
* `@mobile`. Which of those keys are active depends on the breakpoints in
* global settings, but whether any of them exist at all does not, so this
* can be answered without resolving settings.
*/
$has_state_child_layout = false;
foreach ( (array) $style_attr as $state_styles ) {
if ( is_array( $state_styles ) && ! empty( $state_styles['layout'] ) ) {
$has_state_child_layout = true;
break;
}
}
Comment thread
ramonjd marked this conversation as resolved.
Outdated

/*
* Bail out before resolving global settings when the block cannot produce
* layout output. Resolving settings is not read-only: on a cold cache it
* queries the user's `wp_global_styles` post, which fires `the_posts`. A
* callback on that hook that renders blocks would re-enter this filter and,
* without this early return, recurse without a base case.
*/
if ( ! $block_supports_layout && ! $child_layout && ! $has_state_child_layout ) {
return $block_content;
}

Comment thread
ramonjd marked this conversation as resolved.
$global_settings = wp_get_global_settings();
$viewport_settings = $global_settings['viewport'] ?? null;
$responsive_media_queries = WP_Theme_JSON::get_viewport_media_queries( $viewport_settings );
$child_layout = $style_attr['layout'] ?? null;

/*
* Collect responsive viewport child layout overrides so that a block with
Expand Down
73 changes: 73 additions & 0 deletions tests/phpunit/tests/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -1124,4 +1124,77 @@ public function test_layout_support_flag_with_non_string_class_name() {
'Layout support should render the expected markup when className is not a string'
);
}

/**
* Tests that layout support returns early, without resolving global settings,
* for a block that cannot produce any layout output.
*
* Resolving global settings reads the user's `wp_global_styles` post with a
* `WP_Query`, which fires `the_posts`. A callback on that hook that renders
* blocks re-enters this filter, so the bail-out has to happen before the
* lookup or the recursion has no base case.
*
* @ticket 65741
*
* @covers ::wp_render_layout_support_flag
*/
public function test_layout_support_flag_returns_early_before_resolving_global_settings() {
$user_data_resolutions = 0;
add_filter(
'wp_theme_json_data_user',
static function ( $theme_json ) use ( &$user_data_resolutions ) {
++$user_data_resolutions;
return $theme_json;
}
);

// A block with no layout support and no child layout, as produced by
// parsing content that has no block delimiters.
$block_content = '<p>Not a block.</p>';
$block = array(
'blockName' => null,
'attrs' => array(),
);
Comment thread
ramonjd marked this conversation as resolved.

$this->reset_global_settings();

$this->assertSame(
$block_content,
wp_render_layout_support_flag( $block_content, $block ),
'Block content should be returned unchanged when the block has no layout support.'
);
$this->assertSame(
0,
$user_data_resolutions,
'Global settings should not be resolved for a block that cannot produce layout output.'
);

// A block that does support layout still resolves global settings, which
// confirms the assertion above is not passing because of a warm cache.
$this->reset_global_settings();

wp_render_layout_support_flag(
'<div class="wp-block-group"></div>',
array(
'blockName' => 'core/group',
'attrs' => array( 'layout' => array( 'type' => 'constrained' ) ),
)
);

$this->assertGreaterThan(
0,
$user_data_resolutions,
'Global settings should still be resolved for a block that supports layout.'
);
}

/**
* Drops both layers of global settings caching: the resolver's static
* properties and the array cached by wp_get_global_settings().
*/
private function reset_global_settings() {
WP_Theme_JSON_Resolver::clean_cached_data();
wp_cache_delete( 'wp_get_global_settings_custom', 'theme_json' );
wp_cache_delete( 'wp_get_global_settings_theme', 'theme_json' );
}
Comment thread
ramonjd marked this conversation as resolved.
Outdated
}
Loading