Make WordPress Core

Changeset 59256


Ignore:
Timestamp:
10/18/2024 09:53:45 PM (8 weeks ago)
Author:
joemcgill
Message:

Editor: Cache global styles for blocks.

This caches the generated CSS from block nodes in merged Theme JSON data to avoid repeated costly operations required to compute style properties for blocks. The generated CSS is saved to a transient that expires every hour.

This is a follow-up that reimplements [58334], which was previously reverted in [58710].

Props thekt12, spacedmonkey, pereirinha, mukesh27, isabel_brison, oandregal, andrewserong, ramonjd, joemcgill, costdev, aaronrobertshaw, peterwilsoncc.
Fixes #61679. See #59595.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/global-styles-and-settings.php

    r58797 r59256  
    258258    $tree        = WP_Theme_JSON_Resolver::resolve_theme_file_uris( $tree );
    259259    $block_nodes = $tree->get_styles_block_nodes();
     260
     261    $can_use_cached = ! wp_is_development_mode( 'theme' );
     262    $update_cache   = false;
     263
     264    if ( $can_use_cached ) {
     265        // Hash the merged WP_Theme_JSON data to bust cache on settings or styles change.
     266        $cache_hash = md5( wp_json_encode( $tree->get_raw_data() ) );
     267        $cache_key  = 'wp_styles_for_blocks';
     268        $cached     = get_transient( $cache_key );
     269
     270        // Reset the cached data if there is no value or if the hash has changed.
     271        if ( ! is_array( $cached ) || $cached['hash'] !== $cache_hash ) {
     272            $cached = array(
     273                'hash'   => $cache_hash,
     274                'blocks' => array(),
     275            );
     276
     277            // Update the cache if the hash has changed.
     278            $update_cache = true;
     279        }
     280    }
     281
    260282    foreach ( $block_nodes as $metadata ) {
    261         $block_css = $tree->get_styles_for_block( $metadata );
     283
     284        if ( $can_use_cached ) {
     285            // Use the block name as the key for cached CSS data. Otherwise, use a hash of the metadata.
     286            $cache_node_key = isset( $metadata['name'] ) ? $metadata['name'] : md5( wp_json_encode( $metadata ) );
     287
     288            if ( isset( $cached['blocks'][ $cache_node_key ] ) ) {
     289                $block_css = $cached['blocks'][ $cache_node_key ];
     290            } else {
     291                $block_css                           = $tree->get_styles_for_block( $metadata );
     292                $cached['blocks'][ $cache_node_key ] = $block_css;
     293
     294                // Update the cache if the cache contents have changed.
     295                $update_cache = true;
     296            }
     297        } else {
     298            $block_css = $tree->get_styles_for_block( $metadata );
     299        }
    262300
    263301        if ( ! wp_should_load_separate_core_block_assets() ) {
     
    305343        }
    306344    }
     345
     346    if ( $update_cache ) {
     347        set_transient( $cache_key, $cached );
     348    }
    307349}
    308350
  • trunk/tests/phpunit/tests/theme/wpAddGlobalStylesForBlocks.php

    r58710 r59256  
    1919    private $test_blocks = array();
    2020
     21    /**
     22     * Administrator ID.
     23     *
     24     * @var int
     25     */
     26    private static $administrator_id;
     27
     28    public static function set_up_before_class() {
     29        parent::set_up_before_class();
     30        self::$administrator_id = self::factory()->user->create(
     31            array(
     32                'role'       => 'administrator',
     33                'user_email' => 'administrator@example.com',
     34            )
     35        );
     36    }
     37
    2138    public function set_up() {
    2239        parent::set_up();
     
    7794
    7895    /**
     96     * Tests that the block cache is set for global styles.
     97     *
     98     * @ticket 61679
     99     */
     100    public function test_styles_for_blocks_cache_is_set() {
     101        $this->set_up_third_party_block();
     102
     103        wp_register_style( 'global-styles', false, array(), true, true );
     104
     105        $cache_key                = 'wp_styles_for_blocks';
     106        $styles_for_blocks_before = get_transient( $cache_key );
     107        $this->assertFalse( $styles_for_blocks_before, 'No block styles should be cached yet.' );
     108
     109        wp_add_global_styles_for_blocks();
     110
     111        $styles_for_blocks_after = get_transient( $cache_key );
     112        $this->assertNotEmpty( $styles_for_blocks_after, 'No block styles were cached.' );
     113    }
     114
     115    /**
     116     * Tests that the block cache is skipped when in dev mode for themes.
     117     *
     118     * @ticket 61679
     119     */
     120    public function test_styles_for_blocks_skips_cache_in_dev_mode() {
     121        global $_wp_tests_development_mode;
     122
     123        $orig_dev_mode = $_wp_tests_development_mode;
     124
     125        // Setting development mode to theme should skip the cache.
     126        $_wp_tests_development_mode = 'theme';
     127
     128        wp_register_style( 'global-styles', false, array(), true, true );
     129
     130        // Initial register of global styles.
     131        wp_add_global_styles_for_blocks();
     132
     133        $styles_for_blocks_initial = get_transient( 'wp_styles_for_blocks' );
     134
     135        // Cleanup.
     136        $_wp_tests_development_mode = $orig_dev_mode;
     137
     138        $this->assertFalse( $styles_for_blocks_initial );
     139    }
     140
     141    /**
     142     * Tests that the block cache is updated if the block meta has changed.
     143     *
     144     * @ticket 61679
     145     */
     146    public function test_styles_for_blocks_cache_is_skipped() {
     147        wp_register_style( 'global-styles', false, array(), true, true );
     148
     149        // Initial register of global styles.
     150        wp_add_global_styles_for_blocks();
     151
     152        $styles_for_blocks_initial = get_transient( 'wp_styles_for_blocks' );
     153        $this->assertNotEmpty( $styles_for_blocks_initial, 'Initial cache was not set.' );
     154
     155        $this->set_up_third_party_block();
     156
     157        /*
     158         * Call register of global styles again to ensure the cache is updated.
     159         * In normal conditions, this function is only called once per request.
     160         */
     161        wp_add_global_styles_for_blocks();
     162
     163        $styles_for_blocks_updated = get_transient( 'wp_styles_for_blocks' );
     164        $this->assertNotEmpty( $styles_for_blocks_updated, 'Updated cache was not set.' );
     165
     166        $this->assertNotSame(
     167            $styles_for_blocks_initial,
     168            $styles_for_blocks_updated,
     169            'Block style cache was not updated.'
     170        );
     171    }
     172
     173    /**
     174     * Confirms that `wp_styles_for_blocks` cache is cleared when a user modifies global styles.
     175     * @ticket 61679
     176     */
     177    public function test_styles_for_blocks_cache_is_reset_when_user_styles_change() {
     178        // Only administrators can update the global styles post.
     179        wp_set_current_user( self::$administrator_id );
     180
     181        $this->set_up_third_party_block();
     182
     183        wp_register_style( 'global-styles', false, array(), true, true );
     184        wp_add_global_styles_for_blocks();
     185
     186        $cache_key                = 'wp_styles_for_blocks';
     187        $styles_for_blocks_before = get_transient( $cache_key );
     188
     189        // Update the global styles post.
     190        $post_id     = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
     191        $before      = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( wp_get_theme() );
     192        $old_content = json_decode( $before['post_content'], true );
     193
     194        // Mock a change in the global styles.
     195        $new_content = array_merge(
     196            $old_content,
     197            array(
     198                'styles' => array(
     199                    'elements' => array(
     200                        'button' => array(
     201                            'color' => array(
     202                                'background' => 'orange',
     203                            ),
     204                        ),
     205                    ),
     206                ),
     207            )
     208        );
     209
     210        wp_update_post(
     211            array(
     212                'ID'           => $post_id,
     213                'post_content' => wp_json_encode( $new_content ),
     214            )
     215        );
     216
     217        // Reset the static cache, since this would be reset between requests.
     218        WP_Theme_JSON_Resolver::clean_cached_data();
     219
     220        /*
     221         * Call register of global styles again to ensure the cache is updated.
     222         * In normal conditions, this function is only called once per request.
     223         */
     224        wp_add_global_styles_for_blocks();
     225
     226        $cache_key               = 'wp_styles_for_blocks';
     227        $styles_for_blocks_after = get_transient( $cache_key );
     228
     229        $this->assertNotSame(
     230            $styles_for_blocks_before,
     231            $styles_for_blocks_after,
     232            'Block style cache was not updated.'
     233        );
     234    }
     235
     236    /**
    79237     * @ticket 56915
    80238     * @ticket 61165
Note: See TracChangeset for help on using the changeset viewer.