From 408481d4787f6c591a2dd654f7057b35fe5dfba6 Mon Sep 17 00:00:00 2001 From: Alex Concha Date: Wed, 24 Nov 2021 18:59:21 +0100 Subject: [PATCH] Add caching to WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type() (#36584) * Add caching to WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type Refactor `WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type` to add caching to the `wp_global_styles` post type lookup, and also to remove the duplicate code added in `gutenberg_add_active_global_styles_link`. Fixes #36574 * Add unit tests * Use the correct query filter * Use wp_cache_set instead of wp_cache_add. * Remove the filter when finished. * Simplify the if blocks --- ...class-wp-theme-json-resolver-gutenberg.php | 60 ++++++++++++------- .../rest-active-global-styles.php | 18 +----- phpunit/class-wp-theme-json-resolver-test.php | 34 +++++++++++ 3 files changed, 75 insertions(+), 37 deletions(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 65c05bea029bea..1482a5beb95fcf 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -170,33 +170,49 @@ public static function get_theme_data() { * * It can also create and return a new draft CPT. * - * @param bool $should_create_cpt Whether a new CPT should be created if no one was found. - * False by default. - * @param array $post_status_filter Filter CPT by post status. - * ['publish'] by default, so it only fetches published posts. + * @param WP_Theme $theme The theme object. + * If empty, it defaults to the current theme. + * @param bool $should_create_cpt Whether a new CPT should be created if no one was found. + * False by default. + * @param array $post_status_filter Filter CPT by post status. + * ['publish'] by default, so it only fetches published posts. * * @return array Custom Post Type for the user's origin config. */ - private static function get_user_data_from_custom_post_type( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) { + public static function get_user_data_from_custom_post_type( $theme, $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) { + if ( ! $theme instanceof WP_Theme ) { + $theme = wp_get_theme(); + } $user_cpt = array(); $post_type_filter = 'wp_global_styles'; - $recent_posts = wp_get_recent_posts( - array( - 'numberposts' => 1, - 'orderby' => 'date', - 'order' => 'desc', - 'post_type' => $post_type_filter, - 'post_status' => $post_status_filter, - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'name', - 'terms' => wp_get_theme()->get_stylesheet(), - ), + $args = array( + 'numberposts' => 1, + 'orderby' => 'date', + 'order' => 'desc', + 'post_type' => $post_type_filter, + 'post_status' => $post_status_filter, + 'tax_query' => array( + array( + 'taxonomy' => 'wp_theme', + 'field' => 'name', + 'terms' => $theme->get_stylesheet(), ), - ) + ), ); + $cache_key = sprintf( 'wp_global_styles_%s', md5( serialize( $args ) ) ); + $post_id = wp_cache_get( $cache_key ); + + if ( (int) $post_id > 0 ) { + return get_post( $post_id, ARRAY_A ); + } + + // Special case: '-1' is a results not found. + if ( -1 === $post_id && ! $should_create_cpt ) { + return $user_cpt; + } + + $recent_posts = wp_get_recent_posts( $args ); if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) { $user_cpt = $recent_posts[0]; } elseif ( $should_create_cpt ) { @@ -215,6 +231,8 @@ private static function get_user_data_from_custom_post_type( $should_create_cpt ); $user_cpt = get_post( $cpt_post_id, ARRAY_A ); } + $cache_expiration = $user_cpt ? DAY_IN_SECONDS : HOUR_IN_SECONDS; + wp_cache_set( $cache_key, $user_cpt ? $user_cpt['ID'] : -1, '', $cache_expiration ); return $user_cpt; } @@ -230,7 +248,7 @@ public static function get_user_data() { } $config = array(); - $user_cpt = self::get_user_data_from_custom_post_type(); + $user_cpt = self::get_user_data_from_custom_post_type( wp_get_theme() ); if ( array_key_exists( 'post_content', $user_cpt ) ) { $decoded_data = json_decode( $user_cpt['post_content'], true ); @@ -331,7 +349,7 @@ public static function get_user_custom_post_type_id() { return self::$user_custom_post_type_id; } - $user_cpt = self::get_user_data_from_custom_post_type( true ); + $user_cpt = self::get_user_data_from_custom_post_type( wp_get_theme(), true ); if ( array_key_exists( 'ID', $user_cpt ) ) { self::$user_custom_post_type_id = $user_cpt['ID']; } diff --git a/lib/compat/wordpress-5.9/rest-active-global-styles.php b/lib/compat/wordpress-5.9/rest-active-global-styles.php index 507c93dc5553d3..fef6a5448635e4 100644 --- a/lib/compat/wordpress-5.9/rest-active-global-styles.php +++ b/lib/compat/wordpress-5.9/rest-active-global-styles.php @@ -16,22 +16,8 @@ function gutenberg_add_active_global_styles_link( $response, $theme ) { // This creates a record for the current theme if not existent. $id = WP_Theme_JSON_Resolver_Gutenberg::get_user_custom_post_type_id(); } else { - $wp_query_args = array( - 'post_status' => 'publish', - 'post_type' => 'wp_global_styles', - 'posts_per_page' => 1, - 'no_found_rows' => true, - 'fields' => 'ids', - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'name', - 'terms' => $theme->get_stylesheet(), - ), - ), - ); - $global_styles_query = new WP_Query( $wp_query_args ); - $id = ! empty( $global_styles_query->posts ) ? array_shift( $global_styles_query->posts ) : null; + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type( $theme ); + $id = isset( $user_cpt['ID'] ) ? $user_cpt['ID'] : null; } if ( $id ) { diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index 289a940ace3c28..4b4562f935ec27 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -20,6 +20,7 @@ function setUp() { add_filter( 'theme_root', array( $this, 'filter_set_theme_root' ) ); add_filter( 'stylesheet_root', array( $this, 'filter_set_theme_root' ) ); add_filter( 'template_root', array( $this, 'filter_set_theme_root' ) ); + $this->queries = array(); // Clear caches. wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); @@ -40,6 +41,13 @@ function filter_set_locale_to_polish() { return 'pl_PL'; } + function filter_db_query( $query ) { + if ( preg_match( '#post_type = \'wp_global_styles\'#', $query ) ) { + $this->queries[] = $query; + } + return $query; + } + function test_translations_are_applied() { add_filter( 'locale', array( $this, 'filter_set_locale_to_polish' ) ); load_textdomain( 'block-theme', realpath( __DIR__ . '/data/languages/themes/block-theme-pl_PL.mo' ) ); @@ -259,4 +267,30 @@ function test_merges_child_theme_json_into_parent_theme_json() { ) ); } + + function test_get_user_data_from_custom_post_type_does_not_use_uncached_queries() { + add_filter( 'query', array( $this, 'filter_db_query' ) ); + $query_count = count( $this->queries ); + for ( $i = 0; $i < 3; $i++ ) { + WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type( wp_get_theme() ); + WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); + } + $query_count = count( $this->queries ) - $query_count; + $this->assertEquals( 1, $query_count, 'Only one SQL query should be peformed for multiple invocations of WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type()' ); + + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type( wp_get_theme() ); + $this->assertEmpty( $user_cpt ); + + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type( wp_get_theme(), true ); + $this->assertNotEmpty( $user_cpt ); + + $query_count = count( $this->queries ); + for ( $i = 0; $i < 3; $i++ ) { + WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type( wp_get_theme() ); + WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); + } + $query_count = count( $this->queries ) - $query_count; + $this->assertEquals( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type' ); + remove_filter( 'query', array( $this, 'filter_db_query' ) ); + } }