Editor: Fix performance regression in WP_Theme_JSON_Resolver.

A significant performance regression was added late in WP 6.1 beta cycle when some of the existing caching for `theme.json` processing was removed. The rationale for removing the caching was this code was now used before all the blocks are registered (aka get template data, etc.) and resulted in stale cache that created issues (see [https://github.com/WordPress/gutenberg/issues/44434 Gutenberg Issue 44434] and [https://github.com/WordPress/gutenberg/issues/44619 Gutenberg Issue 44619]). The changes were limited to only reads from the file system. However, it introduced a big impact in performance.

This commit adds caching and checks for blocks with different origins. How? It add caching for the calculated data for core, theme, and user based on the blocks that are registered. If the blocks haven't changed since the last time they were calculated for the origin, the cached data is returned. Otherwise, the data is recalculated and cached.

Essentially, this brings back the previous cache, but refreshing it when the blocks change.

It partially adds unit tests for these changes. Additional tests will be added.

References:
* [https://github.com/WordPress/gutenberg/issues/44772 Performance regression in WP 6.1 for theme.json processing]

Follow-up to [54251], [54399].

Props aristath, oandregal, bernhard-reiter, spacedmonkey, hellofromTonya.
See #56467.
Built from https://develop.svn.wordpress.org/trunk@54493


git-svn-id: http://core.svn.wordpress.org/trunk@54052 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
hellofromTonya 2022-10-11 17:17:13 +00:00
parent 9bde65e7ba
commit 701b803ffe
2 changed files with 92 additions and 16 deletions

View File

@ -20,6 +20,19 @@
#[AllowDynamicProperties]
class WP_Theme_JSON_Resolver {
/**
* Container for keep track of registered blocks.
*
* @since 6.1.0
* @var array
*/
protected static $blocks_cache = array(
'core' => array(),
'blocks' => array(),
'theme' => array(),
'user' => array(),
);
/**
* Container for data coming from core.
*
@ -28,6 +41,14 @@ class WP_Theme_JSON_Resolver {
*/
protected static $core = null;
/**
* Container for data coming from the blocks.
*
* @since 6.1.0
* @var WP_Theme_JSON
*/
protected static $blocks = null;
/**
* Container for data coming from the theme.
*
@ -145,6 +166,10 @@ class WP_Theme_JSON_Resolver {
* @return WP_Theme_JSON Entity that holds core data.
*/
public static function get_core_data() {
if ( null !== static::$core && static::has_same_registered_blocks( 'core' ) ) {
return static::$core;
}
$config = static::read_json_file( __DIR__ . '/theme.json' );
$config = static::translate( $config );
@ -162,6 +187,37 @@ class WP_Theme_JSON_Resolver {
return static::$core;
}
/**
* Checks whether the registered blocks were already processed for this origin.
*
* @since 6.1.0
*
* @param string $origin Data source for which to cache the blocks.
* Valid values are 'core', 'blocks', 'theme', and 'user'.
* @return bool True on success, false otherwise.
*/
protected static function has_same_registered_blocks( $origin ) {
// Bail out if the origin is invalid.
if ( ! isset( static::$blocks_cache[ $origin ] ) ) {
return false;
}
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
// Is there metadata for all currently registered blocks?
$block_diff = array_diff_key( $blocks, static::$blocks_cache[ $origin ] );
if ( empty( $block_diff ) ) {
return true;
}
foreach ( $blocks as $block_name => $block_type ) {
static::$blocks_cache[ $origin ][ $block_name ] = true;
}
return false;
}
/**
* Returns the theme's data.
*
@ -189,19 +245,21 @@ class WP_Theme_JSON_Resolver {
$options = wp_parse_args( $options, array( 'with_supports' => true ) );
$theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
$theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) {
$theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
$theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
/**
* Filters the data provided by the theme for global styles and settings.
*
* @since 6.1.0
*
* @param WP_Theme_JSON_Data Class to access and update the underlying data.
*/
$theme_json = apply_filters( 'theme_json_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) );
$theme_json_data = $theme_json->get_data();
static::$theme = new WP_Theme_JSON( $theme_json_data );
/**
* Filters the data provided by the theme for global styles and settings.
*
* @since 6.1.0
*
* @param WP_Theme_JSON_Data Class to access and update the underlying data.
*/
$theme_json = apply_filters( 'theme_json_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) );
$theme_json_data = $theme_json->get_data();
static::$theme = new WP_Theme_JSON( $theme_json_data );
}
if ( wp_get_theme()->parent() ) {
// Get parent theme.json.
@ -258,7 +316,6 @@ class WP_Theme_JSON_Resolver {
}
$with_theme_supports = new WP_Theme_JSON( $theme_support_data );
$with_theme_supports->merge( static::$theme );
return $with_theme_supports;
}
@ -272,7 +329,12 @@ class WP_Theme_JSON_Resolver {
public static function get_block_data() {
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
$config = array( 'version' => 2 );
if ( null !== static::$blocks && static::has_same_registered_blocks( 'blocks' ) ) {
return static::$blocks;
}
$config = array( 'version' => 2 );
foreach ( $blocks as $block_name => $block_type ) {
if ( isset( $block_type->supports['__experimentalStyle'] ) ) {
$config['styles']['blocks'][ $block_name ] = static::remove_json_comments( $block_type->supports['__experimentalStyle'] );
@ -298,7 +360,8 @@ class WP_Theme_JSON_Resolver {
$theme_json = apply_filters( 'theme_json_blocks', new WP_Theme_JSON_Data( $config, 'blocks' ) );
$config = $theme_json->get_data();
return new WP_Theme_JSON( $config, 'blocks' );
static::$blocks = new WP_Theme_JSON( $config, 'blocks' );
return static::$blocks;
}
/**
@ -407,6 +470,10 @@ class WP_Theme_JSON_Resolver {
* @return WP_Theme_JSON Entity that holds styles for user data.
*/
public static function get_user_data() {
if ( null !== static::$user && static::has_same_registered_blocks( 'user' ) ) {
return static::$user;
}
$config = array();
$user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() );
@ -562,9 +629,18 @@ class WP_Theme_JSON_Resolver {
* @since 5.8.0
* @since 5.9.0 Added the `$user`, `$user_custom_post_type_id`,
* and `$i18n_schema` variables to reset.
* @since 6.1.0 Added the `$blocks` and `$blocks_cache` variables
* 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::$theme = null;
static::$user = null;
static::$user_custom_post_type_id = null;

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.1-beta3-54492';
$wp_version = '6.1-beta3-54493';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.