Skip to content

Commit c75fc6d

Browse files
mmtroandregal
andauthored
Update gutenberg_get_global_stylesheet to use WP_Object_Cache (#45679)
Co-authored-by: André <583546+oandregal@users.noreply.github.com>
1 parent d7e3cc1 commit c75fc6d

5 files changed

+243
-85
lines changed

lib/compat/wordpress-6.1/get-global-styles-and-settings.php

-85
Original file line numberDiff line numberDiff line change
@@ -54,88 +54,3 @@ function ( $item ) {
5454
}
5555
}
5656
}
57-
58-
/**
59-
* Returns the stylesheet resulting of merging core, theme, and user data.
60-
*
61-
* @param array $types Types of styles to load. Optional.
62-
* It accepts 'variables', 'styles', 'presets' as values.
63-
* If empty, it'll load all for themes with theme.json support
64-
* and only [ 'variables', 'presets' ] for themes without theme.json support.
65-
*
66-
* @return string Stylesheet.
67-
*/
68-
function gutenberg_get_global_stylesheet( $types = array() ) {
69-
// Return cached value if it can be used and exists.
70-
// It's cached by theme to make sure that theme switching clears the cache.
71-
$can_use_cached = (
72-
( empty( $types ) ) &&
73-
( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) &&
74-
( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) &&
75-
( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) &&
76-
! is_admin()
77-
);
78-
$transient_name = 'gutenberg_global_styles_' . get_stylesheet();
79-
if ( $can_use_cached ) {
80-
$cached = get_transient( $transient_name );
81-
if ( $cached ) {
82-
return $cached;
83-
}
84-
}
85-
$tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
86-
$supports_theme_json = wp_theme_has_theme_json();
87-
if ( empty( $types ) && ! $supports_theme_json ) {
88-
$types = array( 'variables', 'presets', 'base-layout-styles' );
89-
} elseif ( empty( $types ) ) {
90-
$types = array( 'variables', 'styles', 'presets' );
91-
}
92-
93-
/*
94-
* If variables are part of the stylesheet,
95-
* we add them.
96-
*
97-
* This is so themes without a theme.json still work as before 5.9:
98-
* they can override the default presets.
99-
* See https://core.trac.wordpress.org/ticket/54782
100-
*/
101-
$styles_variables = '';
102-
if ( in_array( 'variables', $types, true ) ) {
103-
/*
104-
* We only use the default, theme, and custom origins.
105-
* This is because styles for blocks origin are added
106-
* at a later phase (render cycle) so we only render the ones in use.
107-
* @see wp_add_global_styles_for_blocks
108-
*/
109-
$origins = array( 'default', 'theme', 'custom' );
110-
$styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins );
111-
$types = array_diff( $types, array( 'variables' ) );
112-
}
113-
114-
/*
115-
* For the remaining types (presets, styles), we do consider origins:
116-
*
117-
* - themes without theme.json: only the classes for the presets defined by core
118-
* - themes with theme.json: the presets and styles classes, both from core and the theme
119-
*/
120-
$styles_rest = '';
121-
if ( ! empty( $types ) ) {
122-
/*
123-
* We only use the default, theme, and custom origins.
124-
* This is because styles for blocks origin are added
125-
* at a later phase (render cycle) so we only render the ones in use.
126-
* @see wp_add_global_styles_for_blocks
127-
*/
128-
$origins = array( 'default', 'theme', 'custom' );
129-
if ( ! $supports_theme_json ) {
130-
$origins = array( 'default' );
131-
}
132-
$styles_rest = $tree->get_stylesheet( $types, $origins );
133-
}
134-
$stylesheet = $styles_variables . $styles_rest;
135-
if ( $can_use_cached ) {
136-
// Cache for a minute.
137-
// This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites.
138-
set_transient( $transient_name, $stylesheet, MINUTE_IN_SECONDS );
139-
}
140-
return $stylesheet;
141-
}

lib/compat/wordpress-6.2/class-wp-theme-json-resolver-6-2.php

+25
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,29 @@ public static function theme_has_support() {
3232
return wp_theme_has_theme_json();
3333
}
3434

35+
/**
36+
* Private method to clean the cached data after an upgrade.
37+
*
38+
* It is hooked into the `upgrader_process_complete` action.
39+
*
40+
* @see default-filters.php
41+
*
42+
* @param WP_Upgrader $upgrader WP_Upgrader instance.
43+
* @param array $options Array of bulk item update data.
44+
*/
45+
public static function _clean_cached_data_upon_upgrading( $upgrader, $options ) {
46+
if ( 'update' !== $options['action'] ) {
47+
return;
48+
}
49+
50+
if (
51+
'core' === $options['type'] ||
52+
'plugin' === $options['type'] ||
53+
// Clean cache only if the active theme was updated.
54+
( 'theme' === $options['type'] && ( isset( $options['themes'][ get_stylesheet() ] ) || isset( $options['themes'][ get_template() ] ) ) )
55+
) {
56+
static::clean_cached_data();
57+
}
58+
}
59+
3560
}

lib/compat/wordpress-6.2/default-filters.php

+10
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,13 @@
2020
add_action( 'switch_theme', 'wp_theme_has_theme_json_clean_cache' );
2121
add_action( 'start_previewing_theme', 'wp_theme_has_theme_json_clean_cache' );
2222
add_action( 'upgrader_process_complete', '_wp_theme_has_theme_json_clean_cache_upon_upgrading_active_theme', 10, 2 );
23+
add_action( 'save_post_wp_global_styles', array( 'WP_Theme_JSON_Resolver_Gutenberg', 'clean_cached_data' ) );
24+
add_action( 'activated_plugin', array( 'WP_Theme_JSON_Resolver_Gutenberg', 'clean_cached_data' ) );
25+
add_action( 'deactivated_plugin', array( 'WP_Theme_JSON_Resolver_Gutenberg', 'clean_cached_data' ) );
26+
add_action( 'upgrader_process_complete', array( 'WP_Theme_JSON_Resolver_Gutenberg', '_clean_cached_data_upon_upgrading', 10, 2 ) );
27+
add_action( 'save_post_wp_global_styles', 'gutenberg_get_global_stylesheet_clean_cache' );
28+
add_action( 'switch_theme', 'gutenberg_get_global_stylesheet_clean_cache' );
29+
add_action( 'start_previewing_theme', 'gutenberg_get_global_stylesheet_clean_cache' );
30+
add_action( 'activated_plugin', 'gutenberg_get_global_stylesheet_clean_cache' );
31+
add_action( 'deactivated_plugin', 'gutenberg_get_global_stylesheet_clean_cache' );
32+
add_action( 'upgrader_process_complete', '_gutenberg_get_global_stylesheet_clean_cache_upon_upgrading', 10, 2 );

lib/compat/wordpress-6.2/get-global-styles-and-settings.php

+110
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,113 @@ function _wp_theme_has_theme_json_clean_cache_upon_upgrading_active_theme( $upgr
7979
}
8080
}
8181
}
82+
83+
/**
84+
* Returns the stylesheet resulting of merging core, theme, and user data.
85+
*
86+
* @param array $types Types of styles to load. Optional.
87+
* It accepts 'variables', 'styles', 'presets' as values.
88+
* If empty, it'll load all for themes with theme.json support
89+
* and only [ 'variables', 'presets' ] for themes without theme.json support.
90+
*
91+
* @return string Stylesheet.
92+
*/
93+
function gutenberg_get_global_stylesheet( $types = array() ) {
94+
// Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme developers workflow.
95+
$can_use_cached = empty( $types ) && ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG );
96+
$cache_key = 'gutenberg_get_global_stylesheet';
97+
$cache_group = 'theme_json';
98+
if ( $can_use_cached ) {
99+
$cached = wp_cache_get( $cache_key, $cache_group );
100+
if ( $cached ) {
101+
return $cached;
102+
}
103+
}
104+
$tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
105+
$supports_theme_json = wp_theme_has_theme_json();
106+
if ( empty( $types ) && ! $supports_theme_json ) {
107+
$types = array( 'variables', 'presets', 'base-layout-styles' );
108+
} elseif ( empty( $types ) ) {
109+
$types = array( 'variables', 'styles', 'presets' );
110+
}
111+
112+
/*
113+
* If variables are part of the stylesheet,
114+
* we add them.
115+
*
116+
* This is so themes without a theme.json still work as before 5.9:
117+
* they can override the default presets.
118+
* See https://core.trac.wordpress.org/ticket/54782
119+
*/
120+
$styles_variables = '';
121+
if ( in_array( 'variables', $types, true ) ) {
122+
/*
123+
* We only use the default, theme, and custom origins.
124+
* This is because styles for blocks origin are added
125+
* at a later phase (render cycle) so we only render the ones in use.
126+
* @see wp_add_global_styles_for_blocks
127+
*/
128+
$origins = array( 'default', 'theme', 'custom' );
129+
$styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins );
130+
$types = array_diff( $types, array( 'variables' ) );
131+
}
132+
133+
/*
134+
* For the remaining types (presets, styles), we do consider origins:
135+
*
136+
* - themes without theme.json: only the classes for the presets defined by core
137+
* - themes with theme.json: the presets and styles classes, both from core and the theme
138+
*/
139+
$styles_rest = '';
140+
if ( ! empty( $types ) ) {
141+
/*
142+
* We only use the default, theme, and custom origins.
143+
* This is because styles for blocks origin are added
144+
* at a later phase (render cycle) so we only render the ones in use.
145+
* @see wp_add_global_styles_for_blocks
146+
*/
147+
$origins = array( 'default', 'theme', 'custom' );
148+
if ( ! $supports_theme_json ) {
149+
$origins = array( 'default' );
150+
}
151+
$styles_rest = $tree->get_stylesheet( $types, $origins );
152+
}
153+
$stylesheet = $styles_variables . $styles_rest;
154+
if ( $can_use_cached ) {
155+
wp_cache_set( $cache_key, $stylesheet, $cache_group );
156+
}
157+
return $stylesheet;
158+
}
159+
160+
/**
161+
* Clean the cache used by the `gutenberg_get_global_stylesheet` function.
162+
*/
163+
function gutenberg_get_global_stylesheet_clean_cache() {
164+
wp_cache_delete( 'gutenberg_get_global_stylesheet', 'theme_json' );
165+
}
166+
167+
/**
168+
* Private function to clean the cache used by the `gutenberg_get_global_stylesheet` function after an upgrade.
169+
*
170+
* It is hooked into the `upgrader_process_complete` action.
171+
*
172+
* @see default-filters.php
173+
*
174+
* @param WP_Upgrader $upgrader WP_Upgrader instance.
175+
* @param array $options Array of bulk item update data.
176+
*/
177+
function _gutenberg_get_global_stylesheet_clean_cache_upon_upgrading( $upgrader, $options ) {
178+
if ( 'update' !== $options['action'] ) {
179+
return;
180+
}
181+
182+
if (
183+
'core' === $options['type'] ||
184+
'plugin' === $options['type'] ||
185+
// Clean cache only if the active theme was updated.
186+
( 'theme' === $options['type'] && ( isset( $options['themes'][ get_stylesheet() ] ) || isset( $options['themes'][ get_template() ] ) ) )
187+
) {
188+
gutenberg_get_global_stylesheet_clean_cache();
189+
}
190+
}
191+
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
/**
3+
* Tests wp_get_global_stylesheet().
4+
*
5+
* @package Gutenberg
6+
*/
7+
8+
class WP_Get_Global_Stylesheet_Test extends WP_UnitTestCase {
9+
10+
/**
11+
* Administrator ID.
12+
*
13+
* @var int
14+
*/
15+
protected static $administrator_id;
16+
17+
/**
18+
* Theme root directory.
19+
*
20+
* @var string
21+
*/
22+
private $theme_root;
23+
24+
/**
25+
* Original theme directory.
26+
*
27+
* @var string
28+
*/
29+
private $orig_theme_dir;
30+
31+
public static function set_up_before_class() {
32+
parent::set_up_before_class();
33+
34+
self::$administrator_id = self::factory()->user->create(
35+
array(
36+
'role' => 'administrator',
37+
'user_email' => 'administrator@example.com',
38+
)
39+
);
40+
}
41+
42+
public function set_up() {
43+
parent::set_up();
44+
45+
$this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
46+
$this->theme_root = realpath( DIR_TESTDATA . '/themedir1' );
47+
48+
// /themes is necessary as theme.php functions assume /themes is the root if there is only one root.
49+
$GLOBALS['wp_theme_directories'] = array( WP_CONTENT_DIR . '/themes', $this->theme_root );
50+
51+
// Set up the new root.
52+
add_filter( 'theme_root', array( $this, 'filter_set_theme_root' ) );
53+
add_filter( 'stylesheet_root', array( $this, 'filter_set_theme_root' ) );
54+
add_filter( 'template_root', array( $this, 'filter_set_theme_root' ) );
55+
56+
// Clear caches.
57+
wp_clean_themes_cache();
58+
unset( $GLOBALS['wp_themes'] );
59+
}
60+
61+
public function tear_down() {
62+
$GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
63+
64+
// Clear up the filters to modify the theme root.
65+
remove_filter( 'theme_root', array( $this, 'filter_set_theme_root' ) );
66+
remove_filter( 'stylesheet_root', array( $this, 'filter_set_theme_root' ) );
67+
remove_filter( 'template_root', array( $this, 'filter_set_theme_root' ) );
68+
69+
wp_clean_themes_cache();
70+
unset( $GLOBALS['wp_themes'] );
71+
72+
parent::tear_down();
73+
}
74+
75+
public function filter_set_theme_root() {
76+
return $this->theme_root;
77+
}
78+
79+
public function test_global_styles_user_cpt_change_invalidates_cached_stylesheet() {
80+
add_filter( 'wp_get_global_stylesheet_can_use_cache', '__return_true' );
81+
switch_theme( 'block-theme' );
82+
wp_set_current_user( self::$administrator_id );
83+
84+
$styles = gutenberg_get_global_stylesheet();
85+
$this->assertStringNotContainsString( 'background-color: hotpink;', $styles );
86+
87+
$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme(), true );
88+
$config = json_decode( $user_cpt['post_content'], true );
89+
$config['styles']['color']['background'] = 'hotpink';
90+
$user_cpt['post_content'] = wp_json_encode( $config );
91+
92+
wp_update_post( $user_cpt, true, false );
93+
94+
$styles = gutenberg_get_global_stylesheet();
95+
$this->assertStringContainsString( 'background-color: hotpink;', $styles );
96+
remove_filter( 'wp_get_global_stylesheet_can_use_cache', '__return_true' );
97+
}
98+
}

0 commit comments

Comments
 (0)