Skip to content
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
8 changes: 2 additions & 6 deletions src/wp-includes/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -1022,8 +1022,6 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
* @return string Filtered block content.
*/
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();
Expand Down Expand Up @@ -1241,10 +1239,8 @@ function wp_render_layout_support_flag( $block_content, $block ) {

// Get default blockGap value from global styles for use in layouts like grid.
// Check style variation first, then block-specific styles, then fall back to root styles.
$block_name = $block['blockName'] ?? '';
if ( null === $global_styles ) {
$global_styles = wp_get_global_styles();
}
$block_name = $block['blockName'] ?? '';
$global_styles = wp_get_global_styles();

// Check if the block has an active style variation with a blockGap value.
// Only check the registry if the className contains a variation class to avoid unnecessary lookups.
Expand Down
62 changes: 43 additions & 19 deletions src/wp-includes/class-wp-theme-json-resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,25 @@ class WP_Theme_JSON_Resolver {
* Container for keep track of registered blocks.
*
* @since 6.1.0
* @since 7.2.0 Added the `merged` entry.
* @var array
*/
protected static $blocks_cache = array(
'core' => array(),
'blocks' => array(),
'theme' => array(),
'user' => array(),
'merged' => array(),
);

/**
* Merged data keyed by origin.
*
* @since 7.2.0
* @var array
*/
protected static $merged = array();

/**
* Container for data coming from core.
*
Expand Down Expand Up @@ -192,9 +202,11 @@ public static function get_core_data() {
* Checks whether the registered blocks were already processed for this origin.
*
* @since 6.1.0
* @since 7.2.0 Added the 'merged' origin.
*
* @param string $origin Data source for which to cache the blocks.
* Valid values are 'core', 'blocks', 'theme', and 'user'.
* Valid values are 'core', 'blocks', 'theme', 'user',
* and 'merged'.
* @return bool True on success, false otherwise.
*/
protected static function has_same_registered_blocks( $origin ) {
Expand Down Expand Up @@ -638,6 +650,7 @@ public static function get_user_data() {
* added the `$origin` parameter.
* @since 6.1.0 Added block data and generation of spacingSizes array.
* @since 6.2.0 Changed ' $origin' parameter values to 'default', 'blocks', 'theme' or 'custom'.
* @since 7.2.0 Caches the merged data per origin for the request and returns a copy of it.
*
* @param string $origin Optional. To what level should we merge data: 'default', 'blocks', 'theme' or 'custom'.
* 'custom' is used as default value as well as fallback value if the origin is unknown.
Expand All @@ -648,25 +661,39 @@ public static function get_merged_data( $origin = 'custom' ) {
_deprecated_argument( __FUNCTION__, '5.9.0' );
}

$result = new WP_Theme_JSON();
$result->merge( static::get_core_data() );
if ( 'default' === $origin ) {
return $result;
if ( ! in_array( $origin, array( 'default', 'blocks', 'theme', 'custom' ), true ) ) {
$origin = 'custom';
}

$result->merge( static::get_block_data() );
if ( 'blocks' === $origin ) {
return $result;
/*
* Discard the merged data when blocks were registered after the last merge.
*
* This check detects new blocks, not theme support changes. Call
* clean_cached_data() after changing theme supports to refresh the merged data.
*/
if ( ! static::has_same_registered_blocks( 'merged' ) ) {
static::$merged = array();
}
if ( isset( static::$merged[ $origin ] ) ) {
return clone static::$merged[ $origin ];
}

$result->merge( static::get_theme_data() );
if ( 'theme' === $origin ) {
return $result;
$result = new WP_Theme_JSON();
$result->merge( static::get_core_data() );
if ( 'default' !== $origin ) {
$result->merge( static::get_block_data() );
}
if ( 'theme' === $origin || 'custom' === $origin ) {
$result->merge( static::get_theme_data() );
}
if ( 'custom' === $origin ) {
$result->merge( static::get_user_data() );
}

$result->merge( static::get_user_data() );
static::$merged[ $origin ] = $result;

return $result;
// Return a clone so callers can modify the result without changing the cache.
return clone $result;
}

/**
Expand Down Expand Up @@ -733,16 +760,13 @@ protected static function get_file_path_from_theme( $file_name, $template = fals
* and `$i18n_schema` variables to reset.
* @since 6.1.0 Added the `$blocks` and `$blocks_cache` variables
* to reset.
* @since 7.2.0 Added the `$merged` variable to reset.
*/
public static function clean_cached_data() {
static::$core = null;
static::$blocks = null;
static::$blocks_cache = array(
'core' => array(),
'blocks' => array(),
'theme' => array(),
'user' => array(),
);
static::$blocks_cache = array_fill_keys( array_keys( static::$blocks_cache ), array() );
static::$merged = array();
static::$theme = null;
static::$user = null;
static::$user_custom_post_type_id = null;
Expand Down
47 changes: 3 additions & 44 deletions src/wp-includes/global-styles-and-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Gets the settings resulting of merging core, theme, and user data.
*
* @since 5.9.0
* @since 7.2.0 Removed the per-request cache; WP_Theme_JSON_Resolver::get_merged_data() caches the merged data.
*
* @param array $path Path to the specific setting to retrieve. Optional.
* If empty, will return all settings.
Expand All @@ -32,53 +33,13 @@ function wp_get_global_settings( $path = array(), $context = array() ) {
$path = $new_path;
}

/*
* This is the default value when no origin is provided or when it is 'all'.
*
* The $origin is used as part of the cache key. Changes here need to account
* for clearing the cache appropriately.
*/
// This is the default value when no origin is provided or when it is 'all'.
$origin = 'custom';
if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) {
$origin = 'theme';
}

/*
* By using the 'theme_json' group, this data is marked to be non-persistent across requests.
* See `wp_cache_add_non_persistent_groups` in src/wp-includes/load.php and other places.
*
* The rationale for this is to make sure derived data from theme.json
* is always fresh from the potential modifications done via hooks
* that can use dynamic data (modify the stylesheet depending on some option,
* settings depending on user permissions, etc.).
* See some of the existing hooks to modify theme.json behavior:
* https://make.wordpress.org/core/2022/10/10/filters-for-theme-json-data/
*
* A different alternative considered was to invalidate the cache upon certain
* events such as options add/update/delete, user meta, etc.
* It was judged not enough, hence this approach.
* See https://github.com/WordPress/gutenberg/pull/45372
*/
$cache_group = 'theme_json';
$cache_key = 'wp_get_global_settings_' . $origin;

/*
* Ignore cache when the development mode is set to 'theme', so it doesn't interfere with the theme
* developer's workflow.
*/
$can_use_cached = ! wp_is_development_mode( 'theme' );

$settings = false;
if ( $can_use_cached ) {
$settings = wp_cache_get( $cache_key, $cache_group );
}

if ( false === $settings ) {
$settings = WP_Theme_JSON_Resolver::get_merged_data( $origin )->get_settings();
if ( $can_use_cached ) {
wp_cache_set( $cache_key, $settings, $cache_group );
}
}
$settings = WP_Theme_JSON_Resolver::get_merged_data( $origin )->get_settings();

return _wp_array_get( $settings, $path, $settings );
}
Expand Down Expand Up @@ -452,8 +413,6 @@ function wp_theme_has_theme_json() {
function wp_clean_theme_json_cache() {
wp_cache_delete( 'wp_get_global_stylesheet', 'theme_json' );
wp_cache_delete( 'wp_get_global_styles_svg_filters', 'theme_json' );
wp_cache_delete( 'wp_get_global_settings_custom', 'theme_json' );
wp_cache_delete( 'wp_get_global_settings_theme', 'theme_json' );
wp_cache_delete( 'wp_get_global_styles_custom_css', 'theme_json' );
wp_cache_delete( 'wp_get_theme_data_template_parts', 'theme_json' );
WP_Theme_JSON_Resolver::clean_cached_data();
Expand Down
49 changes: 49 additions & 0 deletions tests/phpunit/tests/theme/wpGetGlobalSettings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* Tests wp_get_global_settings().
*
* @group themes
*
* @covers ::wp_get_global_settings
*/
class Tests_Theme_wpGetGlobalSettings extends WP_UnitTestCase {

public function tear_down(): void {
// Required to reset properties in WP_Theme_JSON_Resolver.
wp_clean_theme_json_cache();
parent::tear_down();
}

Comment thread
sirreal marked this conversation as resolved.
/**
* @ticket 66280
*/
public function test_reflects_theme_data_changed_after_a_previous_call(): void {
wp_get_global_settings();

// Block registration adds no settings, so inject one through the theme data
// filter: registering a block refreshes the theme data, which reapplies the filter.
$filter = static function ( WP_Theme_JSON_Data $theme_json ): WP_Theme_JSON_Data {
return $theme_json->update_with(
array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'settings' => array(
'custom' => array( 'cacheProbe' => 'fresh' ),
),
)
);
};
add_filter( 'wp_theme_json_data_theme', $filter );
register_block_type( 'test/block-settings' );

$settings = wp_get_global_settings();

unregister_block_type( 'test/block-settings' );

$this->assertSame(
'fresh',
$settings['custom']['cacheProbe'] ?? null,
'Settings from theme data changed after a previous call should be present.'
);
}
}
44 changes: 44 additions & 0 deletions tests/phpunit/tests/theme/wpGetGlobalStyles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* Tests wp_get_global_styles().
*
* @group themes
*
* @covers ::wp_get_global_styles
*/
class Tests_Theme_wpGetGlobalStyles extends WP_UnitTestCase {

public function tear_down(): void {
wp_clean_theme_json_cache();
parent::tear_down();
}

/**
* @ticket 66280
*/
public function test_reflects_blocks_registered_after_a_previous_call(): void {
wp_get_global_styles();

register_block_type(
'test/block-gap',
array(
'supports' => array(
'__experimentalStyle' => array(
'spacing' => array( 'blockGap' => '77px' ),
),
),
)
);

$styles = wp_get_global_styles();

unregister_block_type( 'test/block-gap' );

$this->assertSame(
'77px',
$styles['blocks']['test/block-gap']['spacing']['blockGap'] ?? null,
'Styles for a block registered after a previous call should be present.'
);
}
}
Loading
Loading